diff --git a/assets/icons/arrow_right_2.svg b/assets/icons/arrow_right_2.svg
new file mode 100644
index 00000000..1a5adf44
--- /dev/null
+++ b/assets/icons/arrow_right_2.svg
@@ -0,0 +1,4 @@
+
diff --git a/ios/Podfile b/ios/Podfile
index 1e8c3c90..5f4742cd 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -29,6 +29,7 @@ flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
+ pod 'SQLite.swift'
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index efa2db40..75624901 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -78,6 +78,9 @@ PODS:
- sqflite (0.0.2):
- Flutter
- FMDB (>= 2.7.5)
+ - SQLite.swift (0.13.0):
+ - SQLite.swift/standard (= 0.13.0)
+ - SQLite.swift/standard (0.13.0)
- Toast (4.0.0)
- url_launcher (0.0.1):
- Flutter
@@ -103,6 +106,7 @@ DEPENDENCIES:
- permission_handler (from `.symlinks/plugins/permission_handler/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
+ - SQLite.swift
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
- video_player (from `.symlinks/plugins/video_player/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
@@ -118,6 +122,7 @@ SPEC REPOS:
- Reachability
- SDWebImage
- SDWebImageFLPlugin
+ - SQLite.swift
- Toast
EXTERNAL SOURCES:
@@ -181,12 +186,13 @@ SPEC CHECKSUMS:
SDWebImageFLPlugin: 6c2295fb1242d44467c6c87dc5db6b0a13228fd8
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
+ SQLite.swift: 8add701609fd0ef78d097dcc75d20a9782e6f5fc
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
wakelock: b0843b2479edbf6504d8d262c2959446f35373aa
webview_flutter: 9f491a9b5a66f2573946a389b2677987b0ff8c0b
-PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
+PODFILE CHECKSUM: c990cab2bdc393dc88aba776a5ef266622e941de
COCOAPODS: 1.10.1
diff --git a/lib/app/locator.config.dart b/lib/app/locator.config.dart
index fad46db7..c5531b5f 100644
--- a/lib/app/locator.config.dart
+++ b/lib/app/locator.config.dart
@@ -8,7 +8,7 @@ import 'package:get_it/get_it.dart' as _i1;
import 'package:injectable/injectable.dart' as _i2;
import '../services/connectivity_service.dart' as _i7;
-import '../services/storage_service.dart' as _i19;
+import '../services/storage_service.dart' as _i18;
import '../views/awesome_bar/awesome_bar_viewmodel.dart' as _i6;
import '../views/desk/desk_viewmodel.dart' as _i9;
import '../views/form_view/bottom_sheets/assignees/assignees_bottom_sheet_viewmodel.dart'
@@ -16,26 +16,25 @@ import '../views/form_view/bottom_sheets/assignees/assignees_bottom_sheet_viewmo
import '../views/form_view/bottom_sheets/attachments/add_attachments_bottom_sheet_viewmodel.dart'
as _i3;
import '../views/form_view/bottom_sheets/attachments/view_attachments_bottom_sheet_viewmodel.dart'
- as _i21;
+ as _i20;
import '../views/form_view/bottom_sheets/reviews/add_review_bottom_sheet_viewmodel.dart'
as _i4;
import '../views/form_view/bottom_sheets/reviews/view_reviews_bottom_sheet_viewmodel.dart'
- as _i22;
+ as _i21;
import '../views/form_view/bottom_sheets/share/share_bottom_sheet_viewmodel.dart'
- as _i17;
+ as _i16;
import '../views/form_view/bottom_sheets/tags/tags_bottom_sheet_viewmodel.dart'
- as _i20;
-import '../views/form_view/form_view_viewmodel.dart' as _i12;
+ as _i19;
import '../views/list_view/bottom_sheets/edit_filter_bottom_sheet_viewmodel.dart'
as _i10;
import '../views/list_view/bottom_sheets/filters_bottom_sheet_viewmodel.dart'
as _i11;
import '../views/list_view/bottom_sheets/sort_by_fields_bottom_sheet_viewmodel.dart'
- as _i18;
-import '../views/list_view/list_view_viewmodel.dart' as _i13;
-import '../views/login/login_viewmodel.dart' as _i14;
-import '../views/new_doc/new_doc_viewmodel.dart' as _i15;
-import '../views/send_email/send_email_viewmodel.dart' as _i16;
+ as _i17;
+import '../views/list_view/list_view_viewmodel.dart' as _i12;
+import '../views/login/login_viewmodel.dart' as _i13;
+import '../views/new_doc/new_doc_viewmodel.dart' as _i14;
+import '../views/send_email/send_email_viewmodel.dart' as _i15;
import '../widgets/custom_form.dart'
as _i8; // ignore_for_file: unnecessary_lambdas
@@ -58,21 +57,20 @@ _i1.GetIt $initGetIt(_i1.GetIt get,
() => _i10.EditFilterBottomSheetViewModel());
gh.lazySingleton<_i11.FiltersBottomSheetViewModel>(
() => _i11.FiltersBottomSheetViewModel());
- gh.lazySingleton<_i12.FormViewViewModel>(() => _i12.FormViewViewModel());
- gh.lazySingleton<_i13.ListViewViewModel>(() => _i13.ListViewViewModel());
- gh.lazySingleton<_i14.LoginViewModel>(() => _i14.LoginViewModel());
- gh.lazySingleton<_i15.NewDocViewModel>(() => _i15.NewDocViewModel());
- gh.lazySingleton<_i16.SendEmailViewModel>(() => _i16.SendEmailViewModel());
- gh.lazySingleton<_i17.ShareBottomSheetViewModel>(
- () => _i17.ShareBottomSheetViewModel());
- gh.lazySingleton<_i18.SortByFieldsBottomSheetViewModel>(
- () => _i18.SortByFieldsBottomSheetViewModel());
- gh.lazySingleton<_i19.StorageService>(() => _i19.StorageService());
- gh.lazySingleton<_i20.TagsBottomSheetViewModel>(
- () => _i20.TagsBottomSheetViewModel());
- gh.lazySingleton<_i21.ViewAttachmenetsBottomSheetViewModel>(
- () => _i21.ViewAttachmenetsBottomSheetViewModel());
- gh.lazySingleton<_i22.ViewReviewsBottomSheetViewModel>(
- () => _i22.ViewReviewsBottomSheetViewModel());
+ gh.lazySingleton<_i12.ListViewViewModel>(() => _i12.ListViewViewModel());
+ gh.lazySingleton<_i13.LoginViewModel>(() => _i13.LoginViewModel());
+ gh.lazySingleton<_i14.NewDocViewModel>(() => _i14.NewDocViewModel());
+ gh.lazySingleton<_i15.SendEmailViewModel>(() => _i15.SendEmailViewModel());
+ gh.lazySingleton<_i16.ShareBottomSheetViewModel>(
+ () => _i16.ShareBottomSheetViewModel());
+ gh.lazySingleton<_i17.SortByFieldsBottomSheetViewModel>(
+ () => _i17.SortByFieldsBottomSheetViewModel());
+ gh.lazySingleton<_i18.StorageService>(() => _i18.StorageService());
+ gh.lazySingleton<_i19.TagsBottomSheetViewModel>(
+ () => _i19.TagsBottomSheetViewModel());
+ gh.lazySingleton<_i20.ViewAttachmenetsBottomSheetViewModel>(
+ () => _i20.ViewAttachmenetsBottomSheetViewModel());
+ gh.lazySingleton<_i21.ViewReviewsBottomSheetViewModel>(
+ () => _i21.ViewReviewsBottomSheetViewModel());
return get;
}
diff --git a/lib/config/frappe_icons.dart b/lib/config/frappe_icons.dart
index ca5a0aab..e907fbf3 100644
--- a/lib/config/frappe_icons.dart
+++ b/lib/config/frappe_icons.dart
@@ -33,6 +33,7 @@ class FrappeIcons {
static const home_outlined = '$_basePath/home_outlined.svg';
static const select = '$_basePath/select.svg';
static const arrow_right = '$_basePath/arrow_right.svg';
+ static const arrow_right_2 = '$_basePath/arrow_right_2.svg';
static const email = '$_basePath/email.svg';
static const unlock = '$_basePath/unlock.svg';
static const sort_ascending = '$_basePath/sort_ascending.svg';
diff --git a/lib/form/controls/control.dart b/lib/form/controls/control.dart
index 89064aba..c27dcdde 100644
--- a/lib/form/controls/control.dart
+++ b/lib/form/controls/control.dart
@@ -3,6 +3,7 @@ import 'package:frappe_app/config/frappe_palette.dart';
import 'package:frappe_app/config/palette.dart';
import 'package:frappe_app/form/controls/currency.dart';
+import 'package:frappe_app/form/controls/dynamic_link.dart';
import 'package:frappe_app/form/controls/read_only.dart';
import 'package:frappe_app/form/controls/text.dart';
import 'package:frappe_app/model/common.dart';
@@ -47,6 +48,16 @@ Widget makeControl({
}
break;
+ case "Dynamic Link":
+ {
+ control = DynamicLink(
+ doctypeField: field,
+ doc: doc,
+ onControlChanged: onControlChanged,
+ );
+ }
+ break;
+
case "Autocomplete":
{
control = AutoComplete(
diff --git a/lib/form/controls/custom_table.dart b/lib/form/controls/custom_table.dart
index d209a78c..f9d5efc7 100644
--- a/lib/form/controls/custom_table.dart
+++ b/lib/form/controls/custom_table.dart
@@ -19,7 +19,7 @@ class CustomTable extends StatelessWidget {
name: doctypeField.fieldname,
context: context,
doctype: doctypeField.options,
- value: doc[doctypeField.fieldname],
+ initialValue: doc[doctypeField.fieldname],
);
}
}
diff --git a/lib/form/controls/date.dart b/lib/form/controls/date.dart
index d8800853..0430c5d5 100644
--- a/lib/form/controls/date.dart
+++ b/lib/form/controls/date.dart
@@ -1,5 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
+import 'package:frappe_app/model/offline_storage.dart';
+import 'package:frappe_app/model/system_settings_response.dart';
+import 'package:frappe_app/utils/constants.dart';
+import 'package:intl/intl.dart';
import '../../config/palette.dart';
import '../../model/doctype_response.dart';
@@ -32,12 +36,19 @@ class Date extends StatelessWidget with Control, ControlInput {
);
}
+ var dateFormat = SystemSettingsResponse.fromJson(
+ OfflineStorage.getItem("systemSettings")["data"],
+ ).message.defaults.dateFormat;
+
return FormBuilderDateTimePicker(
key: key,
inputType: InputType.date,
valueTransformer: (val) {
return val?.toIso8601String();
},
+ format: DateFormat(
+ Constants.frappeFlutterDateFormatMapping[dateFormat],
+ ),
initialValue:
doc != null ? parseDate(doc![doctypeField.fieldname]) : null,
keyboardType: TextInputType.number,
diff --git a/lib/form/controls/dynamic_link.dart b/lib/form/controls/dynamic_link.dart
new file mode 100644
index 00000000..a73fa6fe
--- /dev/null
+++ b/lib/form/controls/dynamic_link.dart
@@ -0,0 +1,200 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_form_builder/flutter_form_builder.dart';
+import 'package:flutter_typeahead/flutter_typeahead.dart';
+import 'package:frappe_app/config/frappe_icons.dart';
+import 'package:frappe_app/config/palette.dart';
+import 'package:frappe_app/model/common.dart';
+import 'package:frappe_app/utils/frappe_icon.dart';
+import 'package:frappe_app/views/form_view/form_view.dart';
+import 'package:frappe_app/widgets/form_builder_typeahead.dart';
+import 'package:persistent_bottom_nav_bar/persistent-tab-view.dart';
+
+import '../../model/doctype_response.dart';
+import '../../app/locator.dart';
+import '../../services/api/api.dart';
+
+import '../../utils/helpers.dart';
+import '../../model/offline_storage.dart';
+
+import 'base_control.dart';
+import 'base_input.dart';
+
+class DynamicLink extends StatefulWidget {
+ final DoctypeField doctypeField;
+ final Map doc;
+ final OnControlChanged? onControlChanged;
+
+ final key;
+ final bool showInputBorder;
+ final Function? onSuggestionSelected;
+ final Function? noItemsFoundBuilder;
+ final Widget? prefixIcon;
+ final ItemBuilder? itemBuilder;
+ final SuggestionsCallback? suggestionsCallback;
+ final AxisDirection direction;
+ final TextEditingController? controller;
+
+ DynamicLink({
+ this.key,
+ required this.doctypeField,
+ this.onControlChanged,
+ required this.doc,
+ this.prefixIcon,
+ this.onSuggestionSelected,
+ this.noItemsFoundBuilder,
+ this.showInputBorder = false,
+ this.itemBuilder,
+ this.suggestionsCallback,
+ this.controller,
+ this.direction = AxisDirection.down,
+ });
+
+ @override
+ _DynamicLinkState createState() => _DynamicLinkState();
+}
+
+class _DynamicLinkState extends State with Control, ControlInput {
+ @override
+ Widget build(BuildContext context) {
+ List validators = [];
+ var f = setMandatory(widget.doctypeField);
+ late bool enabled;
+
+ if (f != null) {
+ validators.add(
+ f(context),
+ );
+ }
+
+ // if (widget.doctypeField.readOnly == 1 ||
+ // (widget.doc != null && widget.doctypeField.setOnlyOnce == 1)) {
+ // enabled = false;
+ // } else {
+ // enabled = true;
+ // }
+
+ if (widget.doc[widget.doctypeField.fieldname] != null &&
+ widget.doctypeField.setOnlyOnce == 1) {
+ enabled = false;
+ } else {
+ enabled = true;
+ }
+
+ return Theme(
+ data: Theme.of(context).copyWith(primaryColor: Colors.black),
+ child: FormBuilderTypeAhead(
+ key: widget.key,
+ enabled: enabled,
+ onChanged: (val) {
+ if (widget.onControlChanged != null) {
+ widget.onControlChanged!(
+ FieldValue(
+ field: widget.doctypeField,
+ value: val,
+ ),
+ );
+ }
+ },
+ controller: widget.controller,
+ initialValue: widget.doc[widget.doctypeField.fieldname],
+ direction: AxisDirection.up,
+ onSuggestionSelected: (item) {
+ if (widget.onSuggestionSelected != null) {
+ if (item is String) {
+ widget.onSuggestionSelected!(item);
+ } else if (item is Map) {
+ widget.onSuggestionSelected!(item["value"]);
+ }
+ }
+ },
+ validator: FormBuilderValidators.compose(validators),
+ decoration: Palette.formFieldDecoration(
+ label: widget.doctypeField.label,
+ suffixIcon: widget.doc[widget.doctypeField.fieldname] != null &&
+ widget.doc[widget.doctypeField.fieldname] != ""
+ ? IconButton(
+ onPressed: () {
+ pushNewScreen(
+ context,
+ screen: FormView(
+ doctype: widget.doc[widget.doctypeField.options],
+ name: widget.doc[widget.doctypeField.fieldname]),
+ );
+ },
+ icon: FrappeIcon(
+ FrappeIcons.arrow_right_2,
+ size: 14,
+ ),
+ )
+ : null,
+ ),
+ selectionToTextTransformer: (item) {
+ if (item != null) {
+ if (item is Map) {
+ return item["value"];
+ }
+ }
+ return item.toString();
+ },
+ name: widget.doctypeField.fieldname,
+ itemBuilder: widget.itemBuilder ??
+ (context, item) {
+ if (item is Map) {
+ return ListTile(
+ title: Text(
+ item["value"],
+ ),
+ subtitle: item["description"] != null
+ ? Text(
+ item["description"],
+ )
+ : null,
+ );
+ } else {
+ return ListTile(
+ title: Text(item.toString()),
+ );
+ }
+ },
+ suggestionsCallback: widget.suggestionsCallback ??
+ (query) async {
+ var lowercaseQuery = query.toLowerCase();
+ // var isOnline = await verifyOnline();
+ var isOnline = true;
+ if (!isOnline) {
+ var linkFull = await OfflineStorage.getItem(
+ '${widget.doctypeField.options}LinkFull');
+ linkFull = linkFull["data"];
+
+ if (linkFull != null) {
+ return linkFull["results"].where(
+ (link) {
+ return (link["value"] as String)
+ .toLowerCase()
+ .contains(lowercaseQuery);
+ },
+ ).toList();
+ } else {
+ var queryLink = await OfflineStorage.getItem(
+ '$lowercaseQuery${widget.doctypeField.options}Link');
+ queryLink = queryLink["data"];
+
+ if (queryLink != null) {
+ return queryLink["results"];
+ } else {
+ return [];
+ }
+ }
+ } else {
+ var response = await locator().searchLink(
+ doctype: widget.doc[widget.doctypeField.options],
+ txt: lowercaseQuery,
+ );
+
+ return response["results"];
+ }
+ },
+ ),
+ );
+ }
+}
diff --git a/lib/form/controls/link_field.dart b/lib/form/controls/link_field.dart
index 75f1bdb7..3aac3d84 100644
--- a/lib/form/controls/link_field.dart
+++ b/lib/form/controls/link_field.dart
@@ -1,10 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
+import 'package:frappe_app/config/frappe_icons.dart';
import 'package:frappe_app/config/palette.dart';
import 'package:frappe_app/model/common.dart';
+import 'package:frappe_app/utils/frappe_icon.dart';
+import 'package:frappe_app/views/form_view/form_view.dart';
import 'package:frappe_app/widgets/form_builder_typeahead.dart';
-import 'package:provider/provider.dart';
+import 'package:persistent_bottom_nav_bar/persistent-tab-view.dart';
import '../../model/doctype_response.dart';
import '../../app/locator.dart';
@@ -12,7 +15,6 @@ import '../../services/api/api.dart';
import '../../utils/helpers.dart';
import '../../model/offline_storage.dart';
-import '../../utils/enums.dart';
import 'base_control.dart';
import 'base_input.dart';
@@ -109,6 +111,23 @@ class _LinkFieldState extends State with Control, ControlInput {
validator: FormBuilderValidators.compose(validators),
decoration: Palette.formFieldDecoration(
label: widget.doctypeField.label,
+ suffixIcon: widget.doc?[widget.doctypeField.fieldname] != null &&
+ widget.doc?[widget.doctypeField.fieldname] != ""
+ ? IconButton(
+ onPressed: () {
+ pushNewScreen(
+ context,
+ screen: FormView(
+ doctype: widget.doctypeField.options,
+ name: widget.doc![widget.doctypeField.fieldname]),
+ );
+ },
+ icon: FrappeIcon(
+ FrappeIcons.arrow_right_2,
+ size: 14,
+ ),
+ )
+ : null,
),
selectionToTextTransformer: (item) {
if (item != null) {
diff --git a/lib/form/controls/time.dart b/lib/form/controls/time.dart
index d44b619f..7ccc03f1 100644
--- a/lib/form/controls/time.dart
+++ b/lib/form/controls/time.dart
@@ -31,13 +31,24 @@ class Time extends StatelessWidget with Control, ControlInput {
);
}
+ var initialValue;
+
+ if (doc != null) {
+ var value = doc![doctypeField.fieldname];
+
+ if (value != null) {
+ if ((value as String).contains("T")) {
+ value = doc![doctypeField.fieldname].split("T")[1];
+ }
+ initialValue = DateFormat.Hms().parse(
+ value,
+ );
+ }
+ }
+
return FormBuilderDateTimePicker(
key: key,
- initialValue: doc != null
- ? DateFormat.Hms().parse(
- doc![doctypeField.fieldname],
- )
- : null,
+ initialValue: initialValue,
inputType: InputType.time,
valueTransformer: (val) {
return val?.toIso8601String();
diff --git a/lib/model/doctype_response.dart b/lib/model/doctype_response.dart
index f68a82c8..c6c34e9d 100644
--- a/lib/model/doctype_response.dart
+++ b/lib/model/doctype_response.dart
@@ -1,8 +1,8 @@
class DoctypeResponse {
late List docs;
- late String? userSettings;
+ late String userSettings;
- DoctypeResponse({required this.docs, this.userSettings});
+ DoctypeResponse({required this.docs, required this.userSettings});
DoctypeResponse.fromJson(Map json) {
if (json['docs'] != null) {
diff --git a/lib/model/login_request.dart b/lib/model/login_request.dart
index 14c9559e..7b8b7219 100644
--- a/lib/model/login_request.dart
+++ b/lib/model/login_request.dart
@@ -4,6 +4,7 @@ class LoginRequest {
late String? cmd;
late String? otp;
late String? tmpId;
+ late String device;
LoginRequest({
this.usr,
@@ -11,6 +12,7 @@ class LoginRequest {
this.cmd,
this.otp,
this.tmpId,
+ this.device = "mobile",
}) : assert(
(usr != null && pwd != null) ||
(cmd != null && otp != null && tmpId != null),
@@ -22,6 +24,7 @@ class LoginRequest {
cmd = json['cmd'];
otp = json['otp'];
tmpId = json['tmp_id'];
+ device = json['device'];
}
Map toJson() {
@@ -31,6 +34,7 @@ class LoginRequest {
data['cmd'] = this.cmd;
data['otp'] = this.otp;
data['tmp_id'] = this.tmpId;
+ data['device'] = this.device;
return data;
}
}
diff --git a/lib/model/system_settings_response.dart b/lib/model/system_settings_response.dart
new file mode 100644
index 00000000..d8d1ccd3
--- /dev/null
+++ b/lib/model/system_settings_response.dart
@@ -0,0 +1,98 @@
+class SystemSettingsResponse {
+ late Message message;
+
+ SystemSettingsResponse({
+ required this.message,
+ });
+
+ SystemSettingsResponse.fromJson(Map json) {
+ message = Message.fromJson(json['message']);
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['message'] = this.message.toJson();
+ return data;
+ }
+}
+
+class Message {
+ late List timezones;
+ late Defaults defaults;
+
+ Message({required this.timezones, required this.defaults});
+
+ Message.fromJson(Map json) {
+ timezones = json['timezones'].cast();
+ defaults = Defaults.fromJson(json['defaults']);
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['timezones'] = this.timezones;
+ data['defaults'] = this.defaults.toJson();
+ return data;
+ }
+}
+
+class Defaults {
+ late String appName;
+ late String timeZone;
+ late String dateFormat;
+ late String timeFormat;
+ late String numberFormat;
+ late String floatPrecision;
+ late String currencyPrecision;
+ late String sessionExpiry;
+ late String sessionExpiryMobile;
+ late String minimumPasswordScore;
+ late String twoFactorMethod;
+ late String otpIssuerName;
+
+ Defaults({
+ required this.appName,
+ required this.timeZone,
+ required this.dateFormat,
+ required this.timeFormat,
+ required this.numberFormat,
+ required this.floatPrecision,
+ required this.currencyPrecision,
+ required this.sessionExpiry,
+ required this.sessionExpiryMobile,
+ required this.minimumPasswordScore,
+ required this.twoFactorMethod,
+ required this.otpIssuerName,
+ });
+
+ Defaults.fromJson(Map json) {
+ appName = json['app_name'];
+ timeZone = json['time_zone'];
+ dateFormat = json['date_format'];
+ timeFormat = json['time_format'];
+ numberFormat = json['number_format'];
+ floatPrecision = json['float_precision'];
+ currencyPrecision = json['currency_precision'];
+ sessionExpiry = json['session_expiry'];
+ sessionExpiryMobile = json['session_expiry_mobile'];
+ minimumPasswordScore = json['minimum_password_score'];
+ twoFactorMethod = json['two_factor_method'];
+ otpIssuerName = json['otp_issuer_name'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['app_name'] = this.appName;
+ data['time_zone'] = this.timeZone;
+ data['date_format'] = this.dateFormat;
+ data['time_format'] = this.timeFormat;
+ data['number_format'] = this.numberFormat;
+ data['float_precision'] = this.floatPrecision;
+ data['currency_precision'] = this.currencyPrecision;
+ data['session_expiry'] = this.sessionExpiry;
+ data['session_expiry_mobile'] = this.sessionExpiryMobile;
+ data['minimum_password_score'] = this.minimumPasswordScore;
+ data['two_factor_method'] = this.twoFactorMethod;
+ data['otp_issuer_name'] = this.otpIssuerName;
+ return data;
+ }
+}
diff --git a/lib/services/api/api.dart b/lib/services/api/api.dart
index e01d4d90..b4b994f7 100644
--- a/lib/services/api/api.dart
+++ b/lib/services/api/api.dart
@@ -3,6 +3,7 @@ import 'package:frappe_app/model/common.dart';
import 'package:frappe_app/model/get_doc_response.dart';
import 'package:frappe_app/model/group_by_count_response.dart';
import 'package:frappe_app/model/login_request.dart';
+import 'package:frappe_app/model/system_settings_response.dart';
import 'package:frappe_app/model/upload_file_response.dart';
import '../../model/doctype_response.dart';
@@ -122,4 +123,12 @@ abstract class Api {
required List currentFilters,
required String field,
});
+
+ Future getReportViewCount({
+ required String doctype,
+ required Map filters,
+ required List fields,
+ });
+
+ Future getSystemSettings();
}
diff --git a/lib/services/api/dio_api.dart b/lib/services/api/dio_api.dart
index 9b5782ce..e2ece3dd 100644
--- a/lib/services/api/dio_api.dart
+++ b/lib/services/api/dio_api.dart
@@ -9,6 +9,7 @@ import 'package:frappe_app/model/common.dart';
import 'package:frappe_app/model/get_doc_response.dart';
import 'package:frappe_app/model/group_by_count_response.dart';
import 'package:frappe_app/model/login_request.dart';
+import 'package:frappe_app/model/system_settings_response.dart';
import 'package:frappe_app/model/upload_file_response.dart';
import '../../model/doctype_response.dart';
@@ -1002,4 +1003,88 @@ class DioApi implements Api {
}
}
}
+
+ Future getReportViewCount({
+ @required String doctype,
+ @required Map filters,
+ @required List fields,
+ }) async {
+ var reqData = {
+ "doctype": doctype,
+ "filters": filters,
+ "fields": fields,
+ "distinct": false,
+ };
+
+ try {
+ final response = await DioHelper.dio.post(
+ '/method/frappe.desk.reportview.get_count',
+ data: reqData,
+ options: Options(
+ contentType: Headers.formUrlEncodedContentType,
+ ),
+ );
+
+ if (response.statusCode == 200) {
+ return response.data["message"];
+ } else if (response.statusCode == HttpStatus.forbidden) {
+ throw ErrorResponse(
+ statusCode: response.statusCode,
+ statusMessage: response.statusMessage,
+ );
+ } else {
+ throw ErrorResponse();
+ }
+ } catch (e) {
+ if (e is DioError) {
+ var error = e.error;
+ if (error is SocketException) {
+ throw ErrorResponse(
+ statusCode: HttpStatus.serviceUnavailable,
+ statusMessage: error.message,
+ );
+ } else {
+ throw ErrorResponse(statusMessage: error.message);
+ }
+ } else {
+ throw ErrorResponse();
+ }
+ }
+ }
+
+ Future getSystemSettings() async {
+ try {
+ final response = await DioHelper.dio.post(
+ '/method/frappe.core.doctype.system_settings.system_settings.load',
+ options: Options(
+ contentType: Headers.formUrlEncodedContentType,
+ ),
+ );
+
+ if (response.statusCode == 200) {
+ return SystemSettingsResponse.fromJson(response.data);
+ } else if (response.statusCode == HttpStatus.forbidden) {
+ throw ErrorResponse(
+ statusCode: response.statusCode,
+ statusMessage: response.statusMessage,
+ );
+ } else {
+ throw ErrorResponse();
+ }
+ } catch (e) {
+ if (e is DioError) {
+ var error = e.error;
+ if (error is SocketException) {
+ throw ErrorResponse(
+ statusCode: HttpStatus.serviceUnavailable,
+ statusMessage: error.message,
+ );
+ } else {
+ throw ErrorResponse(statusMessage: error.message);
+ }
+ } else {
+ throw ErrorResponse();
+ }
+ }
+ }
}
diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart
index 2b88cc1f..8c4c26cf 100644
--- a/lib/utils/constants.dart
+++ b/lib/utils/constants.dart
@@ -16,4 +16,21 @@ class Constants {
// FilterOperator(label: "Not In", value: "not in"),
FilterOperator(label: "Is", value: "is"),
];
+
+ static var filterOperatorLabelMapping = {
+ "like": "Like",
+ "=": "Equals",
+ "!=": "Not Equals",
+ "not like": "Not Like",
+ "is": "Is",
+ };
+
+ static var frappeFlutterDateFormatMapping = {
+ "dd-mm-yyyy": "d-M-y",
+ "yyyy-mm-dd": "y-M-d",
+ "dd/mm/yyyy": "d/M/y",
+ "dd.mm.yyyy": "d.M.y",
+ "mm/dd/yyyy": "M/d/y",
+ "mm-dd-yyyy": "M-d-y",
+ };
}
diff --git a/lib/utils/helpers.dart b/lib/utils/helpers.dart
index ed20ca00..d59f7f0f 100644
--- a/lib/utils/helpers.dart
+++ b/lib/utils/helpers.dart
@@ -80,7 +80,7 @@ String toTitleCase(String str) {
}
DateTime parseDate(val) {
- if (val == null) {
+ if (val == null || val == "") {
return null;
} else if (val == "Today") {
return DateTime.now();
diff --git a/lib/views/base_widget.dart b/lib/views/base_widget.dart
new file mode 100644
index 00000000..98cf3901
--- /dev/null
+++ b/lib/views/base_widget.dart
@@ -0,0 +1,44 @@
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class BaseWidget extends StatefulWidget {
+ final Widget Function(BuildContext context, T model, Widget? child) builder;
+ final T model;
+ final Widget? child;
+ final Function(T)? onModelReady;
+
+ BaseWidget({
+ required this.builder,
+ required this.model,
+ this.child,
+ this.onModelReady,
+ });
+
+ _BaseWidgetState createState() => _BaseWidgetState();
+}
+
+class _BaseWidgetState extends State> {
+ late T model;
+
+ @override
+ void initState() {
+ model = widget.model;
+
+ if (widget.onModelReady != null) {
+ widget.onModelReady!(model);
+ }
+
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return ChangeNotifierProvider(
+ create: (context) => model,
+ child: Consumer(
+ builder: widget.builder,
+ child: widget.child,
+ ),
+ );
+ }
+}
diff --git a/lib/views/desk/desk_viewmodel.dart b/lib/views/desk/desk_viewmodel.dart
index af9791d6..058d6d18 100644
--- a/lib/views/desk/desk_viewmodel.dart
+++ b/lib/views/desk/desk_viewmodel.dart
@@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:frappe_app/model/common.dart';
+import 'package:frappe_app/model/config.dart';
import 'package:frappe_app/utils/frappe_alert.dart';
import 'package:frappe_app/utils/loading_indicator.dart';
@@ -101,6 +102,27 @@ class DeskViewModel extends BaseViewModel {
}
desktopPage = _desktopPage;
+ // TODO
+ // desktopPage.message.shortcuts.items.forEach(
+ // (element) async {
+ // if (element.format != null && element.statsFilter != null) {
+ // var filters = element.statsFilter;
+
+ // filters = filters!.replaceAll(
+ // "frappe.session.user",
+ // Config().userId!,
+ // );
+
+ // var count = await locator().getReportViewCount(
+ // doctype: element.linkTo,
+ // filters: jsonDecode(filters),
+ // fields: [],
+ // );
+
+ // element.format = element.format!.replaceAll("{}", count.toString());
+ // }
+ // },
+ // );
}
getData() async {
@@ -160,7 +182,7 @@ class DeskViewModel extends BaseViewModel {
pushNewScreen(
context,
screen: FormView(
- meta: meta,
+ meta: meta.docs[0],
name: meta.docs[0].name,
),
withNavBar: true,
diff --git a/lib/views/form_view/bottom_sheets/attachments/view_attachments_bottom_sheet_view.dart b/lib/views/form_view/bottom_sheets/attachments/view_attachments_bottom_sheet_view.dart
index 7ad7244c..8a16d0b3 100644
--- a/lib/views/form_view/bottom_sheets/attachments/view_attachments_bottom_sheet_view.dart
+++ b/lib/views/form_view/bottom_sheets/attachments/view_attachments_bottom_sheet_view.dart
@@ -177,7 +177,7 @@ class ViewFilesToAttach extends StatelessWidget {
width: 12,
),
Text(
- filesToUpload[idx].file.name!,
+ filesToUpload[idx].file.name,
)
],
),
diff --git a/lib/views/form_view/form_view.dart b/lib/views/form_view/form_view.dart
index c86ce021..406ce5a5 100644
--- a/lib/views/form_view/form_view.dart
+++ b/lib/views/form_view/form_view.dart
@@ -35,41 +35,33 @@ import '../../utils/enums.dart';
import '../../widgets/custom_form.dart';
import '../../widgets/frappe_button.dart';
+import '../base_widget.dart';
import 'bottom_sheets/reviews/add_review_bottom_sheet_view.dart';
class FormView extends StatelessWidget {
- final String? name;
- final bool queued;
- final Map? queuedData;
- final DoctypeResponse meta;
+ final String name;
+
+ final DoctypeDoc? meta;
+ final String? doctype;
FormView({
- required this.meta,
- this.name,
- this.queued = false,
- this.queuedData,
+ required this.name,
+ this.meta,
+ this.doctype,
});
final GlobalKey _fbKey = GlobalKey();
-
@override
Widget build(BuildContext context) {
- Provider.of(
- context,
- );
- return BaseView(
+ return BaseWidget(
onModelReady: (model) {
- model.communicationOnly = true;
- model.meta = meta;
- model.queued = queued;
- model.queuedData = queuedData;
- model.name = name;
- model.isDirty = false;
- model.getData();
- },
- onModelClose: (model) {
- model.error = null;
+ model.init(
+ doctype: doctype,
+ constName: name,
+ constMeta: meta,
+ );
},
+ model: FormViewViewModel(),
builder: (context, model, child) => model.state == ViewState.busy
? Scaffold(
body: Center(
@@ -87,12 +79,13 @@ class FormView extends StatelessWidget {
model.getData();
});
}
+
var docs = model.formData.docs;
late String status;
if (docs[0]["status"] == null) {
var value = docs[0]["docstatus"];
- if (isSubmittable(meta.docs[0])) {
+ if (isSubmittable(model.meta)) {
if (value == 0) {
value = "Draft";
} else if (value == 1) {
@@ -115,9 +108,14 @@ class FormView extends StatelessWidget {
// var isLikedByUser = likedBy.contains(model.user);
return Scaffold(
+ floatingActionButton: FloatingActionButton(
+ onPressed: () {
+ print("abc${_fbKey.currentState!.value}");
+ },
+ ),
backgroundColor: FrappePalette.grey[50],
appBar: buildAppBar(
- title: '${meta.docs[0].name} Details',
+ title: '${model.meta.name} Details',
actions: [
Padding(
padding: const EdgeInsets.symmetric(
@@ -163,7 +161,7 @@ class FormView extends StatelessWidget {
children: [
Flexible(
child: Text(
- getTitle(meta.docs[0], docs[0]) ?? "",
+ getTitle(model.meta, docs[0]) ?? "",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
@@ -172,28 +170,27 @@ class FormView extends StatelessWidget {
),
),
Indicator.buildStatusButton(
- meta.docs[0].name,
+ model.meta.name,
status,
)
],
),
),
- if (!queued)
- DocInfo(
- name: name!,
- meta: meta.docs[0],
- doc: docs[0],
- doctype: meta.docs[0].name,
- docInfo: model.docinfo!,
- refreshCallback: () {
- model.getData();
- },
- ),
+ DocInfo(
+ name: name,
+ meta: model.meta,
+ doc: docs[0],
+ doctype: model.meta.name,
+ docInfo: model.docinfo!,
+ refreshCallback: () {
+ model.getData();
+ },
+ ),
CustomForm(
onChanged: () {
model.handleFormDataChange();
},
- fields: meta.docs[0].fields.where(
+ fields: model.meta.fields.where(
(field) {
return field.hidden != 1 &&
field.fieldtype != "Column Break";
@@ -202,62 +199,59 @@ class FormView extends StatelessWidget {
formKey: _fbKey,
doc: docs[0],
),
- if (!queued)
- Padding(
- padding: const EdgeInsets.only(
- bottom: 10,
- ),
- child: ListTileTheme(
- tileColor: Colors.white,
- child: CustomExpansionTile(
- maintainState: true,
- title: Text(
- "Add a comment",
- style: TextStyle(
- fontWeight: FontWeight.w700,
- fontSize: 16,
- ),
+ Padding(
+ padding: const EdgeInsets.only(
+ bottom: 10,
+ ),
+ child: ListTileTheme(
+ tileColor: Colors.white,
+ child: CustomExpansionTile(
+ maintainState: true,
+ title: Text(
+ "Add a comment",
+ style: TextStyle(
+ fontWeight: FontWeight.w700,
+ fontSize: 16,
),
- children: [
- Padding(
- padding: const EdgeInsets.only(
- left: 16.0,
- right: 16.0,
- bottom: 24,
- ),
- child: CommentInput(
- name: name!,
- doctype: meta.docs[0].name,
- callback: () {
- model.getDocinfo();
- },
- ),
- )
- ],
),
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 16.0,
+ right: 16.0,
+ bottom: 24,
+ ),
+ child: CommentInput(
+ name: name,
+ doctype: model.meta.name,
+ callback: () {
+ model.getDocinfo();
+ },
+ ),
+ )
+ ],
),
),
- if (!queued)
- Timeline(
- docinfo: model.docinfo!,
- doctype: meta.docs[0].name,
- name: name!,
- communicationOnly: model.communicationOnly,
- switchCallback: (val) {
- model.toggleSwitch(val);
- },
- refreshCallback: () {
- model.getDocinfo();
- },
- emailSubjectField:
- docs[0][meta.docs[0].subjectField] ??
- getTitle(
- meta.docs[0],
- docs[0],
- ),
- emailSenderField: docs[0]
- [meta.docs[0].senderField],
- ),
+ ),
+ Timeline(
+ docinfo: model.docinfo!,
+ doctype: model.meta.name,
+ name: name,
+ communicationOnly: model.communicationOnly,
+ switchCallback: (val) {
+ model.toggleSwitch(val);
+ },
+ refreshCallback: () {
+ model.getDocinfo();
+ },
+ emailSubjectField:
+ docs[0][model.meta.subjectField] ??
+ getTitle(
+ model.meta,
+ docs[0],
+ ),
+ emailSenderField: docs[0][model.meta.senderField],
+ ),
],
),
);
@@ -282,7 +276,6 @@ class FormView extends StatelessWidget {
await model.handleUpdate(
formValue: formValue,
doc: doc,
- queuedData: queuedData,
);
FrappeAlert.infoAlert(
title: 'Changes Saved',
diff --git a/lib/views/form_view/form_view_viewmodel.dart b/lib/views/form_view/form_view_viewmodel.dart
index 9264eccf..3a6e0eec 100644
--- a/lib/views/form_view/form_view_viewmodel.dart
+++ b/lib/views/form_view/form_view_viewmodel.dart
@@ -16,13 +16,10 @@ import '../../utils/enums.dart';
import '../../utils/helpers.dart';
import '../../model/queue.dart';
-@lazySingleton
class FormViewViewModel extends BaseViewModel {
- String? name;
- late DoctypeResponse meta;
- late bool queued;
+ late String name;
+ late DoctypeDoc meta;
late bool isDirty;
- Map? queuedData;
ErrorResponse? error;
late GetDocResponse formData;
@@ -34,6 +31,26 @@ class FormViewViewModel extends BaseViewModel {
notifyListeners();
}
+ init({
+ String? doctype,
+ DoctypeDoc? constMeta,
+ required String constName,
+ }) async {
+ setState(ViewState.busy);
+ communicationOnly = true;
+ name = constName;
+ isDirty = false;
+ if (constMeta == null) {
+ if (doctype != null) {
+ var metaResponse = await locator().getDoctype(doctype);
+ meta = metaResponse.docs[0];
+ }
+ } else {
+ meta = constMeta;
+ }
+ getData();
+ }
+
handleFormDataChange() {
if (!isDirty) {
isDirty = true;
@@ -48,54 +65,51 @@ class FormViewViewModel extends BaseViewModel {
Future getData() async {
setState(ViewState.busy);
- if (queued && queuedData != null) {
- formData = GetDocResponse(
- docs: queuedData!["data"],
- );
- } else {
- try {
- var isOnline = await verifyOnline();
- var doctype = meta.docs[0].name;
- if (!isOnline) {
- var response = OfflineStorage.getItem(
- '$doctype$name',
- );
- response = response["data"];
- if (response != null) {
- formData = GetDocResponse.fromJson(response);
- docinfo = formData.docinfo;
- } else {
- error = ErrorResponse(
- statusCode: HttpStatus.serviceUnavailable,
- );
- }
+ try {
+ // var isOnline = await verifyOnline();
+ var isOnline = true;
+ var doctype = meta.name;
+
+ if (!isOnline) {
+ var response = OfflineStorage.getItem(
+ '$doctype$name',
+ );
+ response = response["data"];
+ if (response != null) {
+ formData = GetDocResponse.fromJson(response);
+ docinfo = formData.docinfo;
} else {
- formData = await locator().getdoc(
- doctype,
- name!,
+ error = ErrorResponse(
+ statusCode: HttpStatus.serviceUnavailable,
);
- docinfo = formData.docinfo;
}
- } catch (e) {
- error = e as ErrorResponse;
+ } else {
+ formData = await locator().getdoc(
+ doctype,
+ name,
+ );
+ docinfo = formData.docinfo;
}
+ } catch (e) {
+ error = e as ErrorResponse;
}
+
setState(ViewState.idle);
}
getDocinfo() async {
- docinfo = await locator().getDocinfo(meta.docs[0].name, name!);
+ docinfo = await locator().getDocinfo(meta.name, name);
notifyListeners();
}
Future handleUpdate({
required Map formValue,
required Map doc,
- required Map? queuedData,
}) async {
LoadingIndicator.loadingWithBackgroundDisabled("Saving");
- var isOnline = await verifyOnline();
+ // var isOnline = await verifyOnline();
+ var isOnline = true;
if (!isOnline) {
// if (queuedData != null) {
// queuedData["data"] = [
@@ -149,7 +163,7 @@ class FormViewViewModel extends BaseViewModel {
try {
var response = await locator().saveDocs(
- meta.docs[0].name,
+ meta.name,
formValue,
);
diff --git a/lib/views/list_view/list_view.dart b/lib/views/list_view/list_view.dart
index de0bac2f..f5497b36 100644
--- a/lib/views/list_view/list_view.dart
+++ b/lib/views/list_view/list_view.dart
@@ -42,6 +42,7 @@ class CustomListView extends StatelessWidget {
return BaseView(
onModelReady: (model) {
model.meta = meta;
+ model.init();
model.getData();
model.getDesktopPage(module);
model.getSortableFields();
diff --git a/lib/views/list_view/list_view_viewmodel.dart b/lib/views/list_view/list_view_viewmodel.dart
index f3af8bcb..5a55c14c 100644
--- a/lib/views/list_view/list_view_viewmodel.dart
+++ b/lib/views/list_view/list_view_viewmodel.dart
@@ -94,6 +94,94 @@ class ListViewViewModel extends BaseViewModel {
bool get hasError => error != null;
+ init() {
+ var userSettings = jsonDecode(meta.userSettings);
+ var userSettingsList = userSettings["List"];
+ var userSettingsReport = userSettings["Report"];
+
+ if (userSettingsList != null &&
+ (userSettingsList["filters"] as List).isNotEmpty) {
+ (userSettingsList["filters"] as List).forEach(
+ (listFilter) {
+ filters.add(
+ Filter(
+ field: meta.docs[0].fields.firstWhere(
+ (metaField) => metaField.fieldname == listFilter[1],
+ orElse: () {
+ return DoctypeField(
+ fieldname: listFilter[1],
+ label: listFilter[1],
+ );
+ },
+ ),
+ filterOperator: FilterOperator(
+ label: Constants.filterOperatorLabelMapping[listFilter[2]]!,
+ value: listFilter[2],
+ ),
+ value: listFilter[3].toString(),
+ ),
+ );
+ },
+ );
+
+ if (userSettingsList["sort_by"] != null) {
+ sortField = meta.docs[0].fields.firstWhere(
+ (metaField) => metaField.fieldname == userSettingsList["sort_by"],
+ orElse: () {
+ return DoctypeField(
+ fieldname: userSettingsList["sort_by"],
+ label: userSettingsList["sort_by"],
+ );
+ },
+ );
+ }
+
+ if (userSettingsList["sort_order"] != null) {
+ sortOrder = userSettingsList["sort_order"];
+ }
+ } else if (userSettingsReport != null &&
+ (userSettingsReport["filters"] as List).isNotEmpty) {
+ (userSettingsReport["filters"] as List).forEach(
+ (reportFilter) {
+ filters.add(
+ Filter(
+ field: meta.docs[0].fields.firstWhere(
+ (metaField) => metaField.fieldname == reportFilter[1],
+ orElse: () {
+ return DoctypeField(
+ fieldname: reportFilter[1],
+ label: reportFilter[1],
+ );
+ },
+ ),
+ filterOperator: FilterOperator(
+ label: Constants.filterOperatorLabelMapping[reportFilter[2]]!,
+ value: reportFilter[2],
+ ),
+ value: reportFilter[3].toString(),
+ ),
+ );
+ },
+ );
+
+ if (userSettingsReport["sort_by"] != null) {
+ sortField = meta.docs[0].fields.firstWhere(
+ (metaField) => metaField.fieldname == userSettingsReport["sort_by"],
+ orElse: () {
+ return DoctypeField(
+ fieldname: userSettingsReport["sort_by"],
+ label: userSettingsReport["sort_by"],
+ );
+ },
+ );
+ }
+
+ if (userSettingsReport["sort_order"] != null) {
+ sortOrder = userSettingsReport["sort_order"];
+ }
+ }
+ }
+
getData() async {
setState(ViewState.busy);
try {
@@ -169,7 +257,7 @@ class ListViewViewModel extends BaseViewModel {
context,
screen: FormView(
name: name,
- meta: meta,
+ meta: meta.docs[0],
),
withNavBar: true,
);
@@ -256,7 +344,7 @@ class ListViewViewModel extends BaseViewModel {
pushNewScreen(
context,
screen: FormView(
- meta: _meta,
+ meta: _meta.docs[0],
name: _meta.docs[0].name,
),
withNavBar: true,
diff --git a/lib/views/login/login_viewmodel.dart b/lib/views/login/login_viewmodel.dart
index 7601f694..d9592a61 100644
--- a/lib/views/login/login_viewmodel.dart
+++ b/lib/views/login/login_viewmodel.dart
@@ -52,6 +52,14 @@ class LoginViewModel extends BaseViewModel {
);
}
+ getSystemSettings() async {
+ var systemSettings = await locator().getSystemSettings();
+ OfflineStorage.putItem(
+ 'systemSettings',
+ systemSettings.toJson(),
+ );
+ }
+
Future login(LoginRequest loginRequest) async {
loginButtonLabel = "Verifying...";
notifyListeners();
@@ -75,6 +83,7 @@ class LoginViewModel extends BaseViewModel {
await cacheAllUsers();
await initAwesomeItems();
await DioHelper.initCookies();
+ getSystemSettings();
loginButtonLabel = "Success";
notifyListeners();
diff --git a/lib/views/new_doc/new_doc_viewmodel.dart b/lib/views/new_doc/new_doc_viewmodel.dart
index 4f95b2bc..294e845b 100644
--- a/lib/views/new_doc/new_doc_viewmodel.dart
+++ b/lib/views/new_doc/new_doc_viewmodel.dart
@@ -95,7 +95,7 @@ class NewDocViewModel extends BaseViewModel {
NavigationHelper.pushReplacement(
context: context,
page: FormView(
- meta: meta,
+ meta: meta.docs[0],
name: response.data["docs"][0]["name"],
),
);
diff --git a/lib/views/queue.dart b/lib/views/queue.dart
index 568fd436..04cf0afa 100644
--- a/lib/views/queue.dart
+++ b/lib/views/queue.dart
@@ -1,136 +1,136 @@
-import 'package:flutter/material.dart';
-import 'package:frappe_app/model/offline_storage.dart';
-import 'package:frappe_app/views/form_view/form_view.dart';
-import 'package:provider/provider.dart';
+// import 'package:flutter/material.dart';
+// import 'package:frappe_app/model/offline_storage.dart';
+// import 'package:frappe_app/views/form_view/form_view.dart';
+// import 'package:provider/provider.dart';
-import '../config/frappe_icons.dart';
-import '../config/palette.dart';
+// import '../config/frappe_icons.dart';
+// import '../config/palette.dart';
-import '../widgets/card_list_tile.dart';
+// import '../widgets/card_list_tile.dart';
-import '../utils/frappe_alert.dart';
-import '../utils/frappe_icon.dart';
-import '../utils/enums.dart';
-import '../utils/helpers.dart';
-import '../model/queue.dart';
+// import '../utils/frappe_alert.dart';
+// import '../utils/frappe_icon.dart';
+// import '../utils/enums.dart';
+// import '../utils/helpers.dart';
+// import '../model/queue.dart';
-class QueueList extends StatefulWidget {
- @override
- _QueueListState createState() => _QueueListState();
-}
+// class QueueList extends StatefulWidget {
+// @override
+// _QueueListState createState() => _QueueListState();
+// }
-class _QueueListState extends State {
- void _refresh() {
- setState(() {});
- }
+// class _QueueListState extends State {
+// void _refresh() {
+// setState(() {});
+// }
- @override
- Widget build(BuildContext context) {
- var connectionStatus = Provider.of(
- context,
- );
+// @override
+// Widget build(BuildContext context) {
+// var connectionStatus = Provider.of(
+// context,
+// );
- return Scaffold(
- backgroundColor: Palette.bgColor,
- appBar: AppBar(
- title: Text('Queue'),
- ),
- body: RefreshIndicator(
- onRefresh: () async {
- _refresh();
- },
- child: Builder(
- builder: (
- context,
- ) {
- var l = Queue.getQueueItems();
- if (l.length < 1) {
- return Padding(
- padding: EdgeInsets.all(8),
- child: Text(
- "Queue is Empty",
- style: TextStyle(
- fontSize: 20,
- color: Palette.secondaryTxtColor,
- fontWeight: FontWeight.bold,
- ),
- ),
- );
- }
- return ListView.builder(
- itemCount: l.length,
- itemBuilder: (context, index) {
- var q = l[index];
- return CardListTile(
- leading: IconButton(
- icon: Icon(Icons.sync),
- onPressed: () async {
- var isOnline = await verifyOnline();
- if (connectionStatus == ConnectivityStatus.offline &&
- !isOnline) {
- FrappeAlert.errorAlert(
- title: 'Cant Sync, App is offline',
- context: context,
- );
- return;
- } else if (q["error"] != null) {
- FrappeAlert.errorAlert(
- title:
- "There was some error while processing this item",
- context: context,
- );
- return;
- }
+// return Scaffold(
+// backgroundColor: Palette.bgColor,
+// appBar: AppBar(
+// title: Text('Queue'),
+// ),
+// body: RefreshIndicator(
+// onRefresh: () async {
+// _refresh();
+// },
+// child: Builder(
+// builder: (
+// context,
+// ) {
+// var l = Queue.getQueueItems();
+// if (l.length < 1) {
+// return Padding(
+// padding: EdgeInsets.all(8),
+// child: Text(
+// "Queue is Empty",
+// style: TextStyle(
+// fontSize: 20,
+// color: Palette.secondaryTxtColor,
+// fontWeight: FontWeight.bold,
+// ),
+// ),
+// );
+// }
+// return ListView.builder(
+// itemCount: l.length,
+// itemBuilder: (context, index) {
+// var q = l[index];
+// return CardListTile(
+// leading: IconButton(
+// icon: Icon(Icons.sync),
+// onPressed: () async {
+// var isOnline = await verifyOnline();
+// if (connectionStatus == ConnectivityStatus.offline &&
+// !isOnline) {
+// FrappeAlert.errorAlert(
+// title: 'Cant Sync, App is offline',
+// context: context,
+// );
+// return;
+// } else if (q["error"] != null) {
+// FrappeAlert.errorAlert(
+// title:
+// "There was some error while processing this item",
+// context: context,
+// );
+// return;
+// }
- await Queue.processQueueItem(q, index);
- _refresh();
- },
- ),
- title: Text(q['title'] ?? ""),
- subtitle: Row(
- children: [
- Text(
- q['doctype'],
- ),
- VerticalDivider(),
- Text(
- q["type"],
- ),
- VerticalDivider(),
- if (q["error"] != null)
- FrappeIcon(
- FrappeIcons.error,
- size: 20,
- ),
- ],
- ),
- trailing: IconButton(
- onPressed: () {
- Queue.deleteAt(index);
- setState(() {});
- },
- icon: Icon(Icons.clear),
- ),
- onTap: () async {
- q["qIdx"] = index;
- var meta = await OfflineStorage.getMeta(q['doctype']);
+// await Queue.processQueueItem(q, index);
+// _refresh();
+// },
+// ),
+// title: Text(q['title'] ?? ""),
+// subtitle: Row(
+// children: [
+// Text(
+// q['doctype'],
+// ),
+// VerticalDivider(),
+// Text(
+// q["type"],
+// ),
+// VerticalDivider(),
+// if (q["error"] != null)
+// FrappeIcon(
+// FrappeIcons.error,
+// size: 20,
+// ),
+// ],
+// ),
+// trailing: IconButton(
+// onPressed: () {
+// Queue.deleteAt(index);
+// setState(() {});
+// },
+// icon: Icon(Icons.clear),
+// ),
+// onTap: () async {
+// q["qIdx"] = index;
+// var meta = await OfflineStorage.getMeta(q['doctype']);
- Navigator.of(context).push(MaterialPageRoute(
- builder: (context) {
- return FormView(
- queued: true,
- queuedData: q,
- meta: meta,
- );
- },
- ));
- },
- );
- },
- );
- },
- ),
- ),
- );
- }
-}
+// Navigator.of(context).push(MaterialPageRoute(
+// builder: (context) {
+// return FormView(
+// queued: true,
+// queuedData: q,
+// meta: meta.docs[0],
+// );
+// },
+// ));
+// },
+// );
+// },
+// );
+// },
+// ),
+// ),
+// );
+// }
+// }
diff --git a/lib/widgets/custom_form.dart b/lib/widgets/custom_form.dart
index c4bf4aae..774897b5 100644
--- a/lib/widgets/custom_form.dart
+++ b/lib/widgets/custom_form.dart
@@ -24,6 +24,9 @@ class CustomForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BaseView(
+ onModelReady: (model) {
+ model.doc = doc;
+ },
builder: (context, model, child) => FormBuilder(
onChanged: () {
if (formKey.currentState != null) {
@@ -41,7 +44,7 @@ class CustomForm extends StatelessWidget {
child: Column(
children: generateLayout(
fields: fields,
- doc: doc,
+ doc: model.doc,
onControlChanged: (v) {},
),
),
@@ -53,7 +56,10 @@ class CustomForm extends StatelessWidget {
@lazySingleton
class CustomFormViewModel extends BaseViewModel {
+ late Map doc;
+
handleFormDataChange(Map formValue) {
- // print(formValue);
+ doc = formValue;
+ notifyListeners();
}
}
diff --git a/lib/widgets/form_builder_table.dart b/lib/widgets/form_builder_table.dart
index e33bfdea..8642ba51 100644
--- a/lib/widgets/form_builder_table.dart
+++ b/lib/widgets/form_builder_table.dart
@@ -15,7 +15,7 @@ class FormBuilderTable extends FormBuilderField {
required String name,
required BuildContext context,
required String doctype,
- required List value,
+ required T initialValue,
Key? key,
FormFieldValidator? validator,
bool enabled = true,
@@ -23,11 +23,13 @@ class FormBuilderTable extends FormBuilderField {
key: key,
name: name,
validator: validator,
+ initialValue: initialValue,
builder: (FormFieldState field) {
return FutureBuilder(
future: locator().getDoctype(doctype),
builder: (context, snapshot) {
if (snapshot.hasData) {
+ var value = (initialValue as List);
var metaFields =
(snapshot.data as DoctypeResponse).docs[0].fields;
var tableFields = metaFields.where((field) {
@@ -87,6 +89,8 @@ class FormBuilderTable extends FormBuilderField {
return TableElement(
doc: val,
fields: tableFields,
+ meta: (snapshot.data as DoctypeResponse)
+ .docs[0],
);
},
),
@@ -232,12 +236,14 @@ class FormBuilderTableState
extends FormBuilderFieldState, T> {}
class TableElement extends StatefulWidget {
+ final DoctypeDoc meta;
final List fields;
final Map doc;
TableElement({
- required this.fields,
+ required this.meta,
required this.doc,
+ required this.fields,
});
@override
@@ -272,7 +278,7 @@ class _TableElementState extends State {
],
),
body: CustomForm(
- fields: widget.fields,
+ fields: widget.meta.fields,
formKey: _fbKey,
doc: widget.doc,
),