From 402f7513bda3b32097a2f0fd72a14597344c59cd Mon Sep 17 00:00:00 2001 From: zaid_daoud Date: Thu, 26 Oct 2023 13:28:51 +0300 Subject: [PATCH] First Action Bottom Sheet Done --- .../api/service_requests_provider.dart | 14 +- lib/l10n/app_ar.arb | 8 +- lib/l10n/app_en.arb | 6 +- lib/main.dart | 4 + .../common_widgets/app_filled_button.dart | 6 +- .../first_action_provider.dart | 36 ++++++ .../loan_availability_provider.dart | 36 ++++++ .../requests/create_service_request_page.dart | 4 +- .../requests/first_action_bottom_sheet.dart | 122 ++++++++++++++++++ .../requests/service_request_details.dart | 117 +++++++++-------- .../widgets/date_and_time/date_picker.dart | 2 +- .../service_request_update_dialog.dart | 5 +- 12 files changed, 288 insertions(+), 72 deletions(-) create mode 100644 lib/providers/service_request_providers/first_action_provider.dart create mode 100644 lib/providers/service_request_providers/loan_availability_provider.dart create mode 100644 lib/views/pages/user/requests/first_action_bottom_sheet.dart diff --git a/lib/controllers/providers/api/service_requests_provider.dart b/lib/controllers/providers/api/service_requests_provider.dart index 847337ae..b00f79b6 100644 --- a/lib/controllers/providers/api/service_requests_provider.dart +++ b/lib/controllers/providers/api/service_requests_provider.dart @@ -233,12 +233,12 @@ class ServiceRequestsProvider extends ChangeNotifier { } } - Future updateDate({ - @required String host, + Future updateRequest({ @required User user, @required ServiceRequest request, - DateTime date, }) async { + isLoading = true; + notifyListeners(); Map serviceRequest = await getServiceRequestById(requestId: request.id) ?? ""; final List callSiteContacts = (serviceRequest['callSiteContactPerson'] as List); final Map callSiteContactPerson = callSiteContacts.isEmpty ? {} : callSiteContacts[0]; @@ -285,8 +285,8 @@ class ServiceRequestsProvider extends ChangeNotifier { "firstAction": request.firstAction?.toJson(), "loanAvailablity": request.loanAvailability?.toJson(), "comments": request.reviewComment, - "firstActionDate": date?.toIso8601String(), - "visitDate": date?.toIso8601String() ?? "", + "firstActionDate": request.visitDate, + "visitDate": request.visitDate, "callReview": null, "reviewComment": null, }; @@ -300,8 +300,12 @@ class ServiceRequestsProvider extends ChangeNotifier { // request.engineerName = employee.name; notifyListeners(); } + isLoading = false; + notifyListeners(); return response.statusCode; } catch (error) { + isLoading = false; + notifyListeners(); return -1; } } diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb index 2bfa3d2a..733a78aa 100644 --- a/lib/l10n/app_ar.arb +++ b/lib/l10n/app_ar.arb @@ -255,7 +255,7 @@ "pickAsset" : "إختر جهاز", "filter" : "تصنيف", "pickAsset" : "إختر جهاز", - "firstAction" : "First Action", + "firstAction" : "الإجراء الأول", "priority" : "الأولوية", "equipmentStatus" : "حالة المعدات", "attachImage" : "إرفاق صورة", @@ -283,5 +283,9 @@ "bySite" : "حسب الموقع", "applyFilter" : "تصفية", "pmDateRange" : "النطاق الزمني للصيانة الوقائية", - "pickDate" : "إختر تاريخ" + "pickDate" : "إختر تاريخ", + "pickADate" : "إختر تاريخ", + "firstActionStatus" : "حالة الإجراء الأول", + "loanAvailability" : "توافر القرض", + "save" : "حفظ" } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index c1faaab6..b7ac86ab 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -288,5 +288,9 @@ "bySite" : "By Site", "applyFilter" : "Apply Filter", "pmDateRange" : "PM Date Range", - "pickDate" : "Pick Date" + "pickDate" : "Pick Date", + "pickADate" : "Pick a date", + "firstActionStatus" : "First Action Status", + "loanAvailability" : "Loan Availability", + "save" : "Save" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 2a015b48..54a74935 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -54,6 +54,8 @@ import 'package:test_sa/providers/gas_request_providers/gas_types_provider.dart' import 'package:test_sa/providers/gas_request_providers/site_provider.dart'; import 'package:test_sa/providers/loading_list_notifier.dart'; import 'package:test_sa/providers/service_request_providers/equipment_status_provider.dart'; +import 'package:test_sa/providers/service_request_providers/first_action_provider.dart'; +import 'package:test_sa/providers/service_request_providers/loan_availability_provider.dart'; import 'package:test_sa/providers/service_request_providers/priority_provider.dart'; import 'package:test_sa/providers/service_request_providers/requested_through_provider.dart'; import 'package:test_sa/providers/service_request_providers/type_of_request_provider.dart'; @@ -166,6 +168,8 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => EquipmentStatusProvider()), ChangeNotifierProvider(create: (_) => RequestedThroughProvider()), ChangeNotifierProvider(create: (_) => TypeOfRequestProvider()), + ChangeNotifierProvider(create: (_) => FirstActionStatusProvider()), + ChangeNotifierProvider(create: (_) => LoanAvailabilityProvider()), ], child: GestureDetector( onTap: () { diff --git a/lib/new_views/common_widgets/app_filled_button.dart b/lib/new_views/common_widgets/app_filled_button.dart index 9f6b9df4..4d681c9a 100644 --- a/lib/new_views/common_widgets/app_filled_button.dart +++ b/lib/new_views/common_widgets/app_filled_button.dart @@ -3,13 +3,12 @@ import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; -import 'package:test_sa/models/enums/translation_keys.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; class AppFilledButton extends StatelessWidget { final VoidCallback onPressed; final String label; - final bool maxWidth; + final bool maxWidth, loading; final Color buttonColor; final Color textColor; @@ -17,6 +16,7 @@ class AppFilledButton extends StatelessWidget { @required this.onPressed, @required this.label, this.maxWidth = false, + this.loading = false, this.buttonColor, this.textColor, Key key, @@ -32,7 +32,7 @@ class AppFilledButton extends StatelessWidget { borderRadius: BorderRadius.circular(10), color: buttonColor ?? Theme.of(context).primaryColor, ), - child: label.heading6(context).custom(color: textColor ?? (context.isDark ? AppColor.neutral60 : Colors.white)), + child: loading ? const CircularProgressIndicator(color: Colors.white) : label.heading6(context).custom(color: textColor ?? (context.isDark ? AppColor.neutral60 : Colors.white)), ).onPress(onPressed); } } diff --git a/lib/providers/service_request_providers/first_action_provider.dart b/lib/providers/service_request_providers/first_action_provider.dart new file mode 100644 index 00000000..7dbeeffe --- /dev/null +++ b/lib/providers/service_request_providers/first_action_provider.dart @@ -0,0 +1,36 @@ +import 'dart:convert'; + +import 'package:http/http.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; + +import '../../controllers/api_routes/api_manager.dart'; +import '../../controllers/api_routes/urls.dart'; +import '../../models/lookup.dart'; + +class FirstActionStatusProvider extends LoadingListNotifier { + @override + Future getDate() async { + if (loading == true) return -2; + loading = true; + notifyListeners(); + loading = true; + notifyListeners(); + try { + Response response = await ApiManager.instance.get(URLs.getServiceFirstAction); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + // client's request was successfully received + List categoriesListJson = json.decode(response.body)["data"]; + items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList(); + } + loading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + loading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } +} diff --git a/lib/providers/service_request_providers/loan_availability_provider.dart b/lib/providers/service_request_providers/loan_availability_provider.dart new file mode 100644 index 00000000..7229acd0 --- /dev/null +++ b/lib/providers/service_request_providers/loan_availability_provider.dart @@ -0,0 +1,36 @@ +import 'dart:convert'; + +import 'package:http/http.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; + +import '../../controllers/api_routes/api_manager.dart'; +import '../../controllers/api_routes/urls.dart'; +import '../../models/lookup.dart'; + +class LoanAvailabilityProvider extends LoadingListNotifier { + @override + Future getDate() async { + if (loading == true) return -2; + loading = true; + notifyListeners(); + loading = true; + notifyListeners(); + try { + Response response = await ApiManager.instance.get(URLs.getServiceLoanAvailability); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + // client's request was successfully received + List categoriesListJson = json.decode(response.body)["data"]; + items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList(); + } + loading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + loading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } +} diff --git a/lib/views/pages/user/requests/create_service_request_page.dart b/lib/views/pages/user/requests/create_service_request_page.dart index 7aa6e510..c04d69d3 100644 --- a/lib/views/pages/user/requests/create_service_request_page.dart +++ b/lib/views/pages/user/requests/create_service_request_page.dart @@ -158,7 +158,7 @@ class CreateServiceRequestPageState extends State { context: context, enabled: false, title: context.translation.requestedThrough, - initialValue: snapshot.items?.firstWhere((element) => element.id == 375, orElse: () => null), + initialValue: snapshot.items?.firstWhere((element) => element.value == 3, orElse: () => null), ); }), 8.height, @@ -211,7 +211,7 @@ class CreateServiceRequestPageState extends State { } Future _submit() async { - _serviceRequest?.requestedThrough = Provider.of(context, listen: false).items?.firstWhere((element) => element.id == 375, orElse: () => null); + _serviceRequest?.requestedThrough = Provider.of(context, listen: false).items?.firstWhere((element) => element.value == 3, orElse: () => null); if (_formKey.currentState.validate() && await _serviceRequest.validateNewRequest(context)) { _formKey.currentState.save(); _serviceRequest.devicePhotos = _deviceImages.map((e) => _isLocalUrl(e.path) ? "${e.path.split("/").last}|${base64Encode(e.readAsBytesSync())}" : e.path).toList(); diff --git a/lib/views/pages/user/requests/first_action_bottom_sheet.dart b/lib/views/pages/user/requests/first_action_bottom_sheet.dart new file mode 100644 index 00000000..fcccef91 --- /dev/null +++ b/lib/views/pages/user/requests/first_action_bottom_sheet.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/user_provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/service_request/service_request.dart'; + +import '../../../../controllers/providers/api/service_requests_provider.dart'; +import '../../../../models/lookup.dart'; +import '../../../../new_views/app_style/app_color.dart'; +import '../../../../new_views/common_widgets/app_filled_button.dart'; +import '../../../../new_views/common_widgets/app_text_form_field.dart'; +import '../../../../new_views/common_widgets/single_item_drop_down_menu.dart'; +import '../../../../providers/service_request_providers/first_action_provider.dart'; +import '../../../../providers/service_request_providers/loan_availability_provider.dart'; +import '../../../widgets/date_and_time/date_picker.dart'; + +class FirstActionBottomSheet extends StatefulWidget { + final ServiceRequest request; + const FirstActionBottomSheet({Key key, @required this.request}) : super(key: key); + + @override + State createState() => _FirstActionBottomSheetState(); +} + +class _FirstActionBottomSheetState extends State { + ServiceRequest _serviceRequest; + final GlobalKey _formKey = GlobalKey(); + @override + void initState() { + super.initState(); + _serviceRequest = widget.request; + } + + @override + Widget build(BuildContext context) { + final userProvider = Provider.of(context, listen: false); + return Container( + height: MediaQuery.of(context).size.height * 0.55, + clipBehavior: Clip.antiAlias, + margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + decoration: BoxDecoration( + color: Theme.of(context).scaffoldBackgroundColor, + borderRadius: const BorderRadius.only(topRight: Radius.circular(20), topLeft: Radius.circular(20)), + ), + padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 8.toScreenHeight), + child: Form( + key: _formKey, + child: Column( + children: [ + Container( + width: 40.toScreenWidth, + height: 5.toScreenHeight, + decoration: BoxDecoration(color: AppColor.neutral40, borderRadius: BorderRadius.circular(30)), + ), + Align( + alignment: AlignmentDirectional.centerStart, + child: context.translation.firstAction.heading3(context).custom(fontWeight: FontWeight.w600).paddingOnly(top: 16, bottom: 16), + ), + SingleItemDropDownMenu( + context: context, + title: context.translation.firstActionStatus, + initialValue: _serviceRequest.firstAction, + onSelect: (value) { + setState(() { + _serviceRequest.firstAction = value; + if (_serviceRequest?.firstAction?.value != 2) { + _serviceRequest.visitDate = null; + } + }); + }, + ), + if (_serviceRequest?.firstAction?.value == 2) 8.height, + if (_serviceRequest?.firstAction?.value == 2) + ADatePicker( + label: context.translation.visitDate, + date: DateTime.tryParse(_serviceRequest.visitDate ?? ""), + onDatePicker: (date) { + setState(() { + _serviceRequest.visitDate = date?.toIso8601String(); + }); + }, + ), + 8.height, + SingleItemDropDownMenu( + context: context, + title: context.translation.loanAvailability, + initialValue: _serviceRequest.loanAvailability, + onSelect: (value) { + _serviceRequest.loanAvailability = value; + }, + ), + 8.height, + AppTextFormField( + labelText: context.translation.comments, + textInputType: TextInputType.multiline, + alignLabelWithHint: true, + onSaved: (text) { + _serviceRequest.comments = text; + }, + ), + 16.height, + Consumer( + builder: (context, snapshot, _) => AppFilledButton( + label: context.translation.save, + loading: snapshot.isLoading, + onPressed: () async { + _formKey.currentState.save(); + await snapshot.updateRequest(user: userProvider.user, request: _serviceRequest); + Navigator.pop(context); + }, + ), + ), + 16.height, + ], + ), + ), + ); + } +} diff --git a/lib/views/pages/user/requests/service_request_details.dart b/lib/views/pages/user/requests/service_request_details.dart index 43564649..1e0f4ecd 100644 --- a/lib/views/pages/user/requests/service_request_details.dart +++ b/lib/views/pages/user/requests/service_request_details.dart @@ -1,39 +1,27 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; -import 'package:test_sa/controllers/localization/localization.dart'; import 'package:test_sa/controllers/providers/api/service_requests_provider.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart'; -import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; -import 'package:test_sa/models/enums/translation_keys.dart'; import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/service_request/service_request.dart'; -import 'package:test_sa/models/subtitle.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; import 'package:test_sa/views/app_style/colors.dart'; import 'package:test_sa/views/app_style/sizing.dart'; -import 'package:test_sa/views/pages/user/requests/create_service_request_page.dart'; -import 'package:test_sa/views/pages/user/requests/work_order/create_service_report.dart'; -import 'package:test_sa/views/pages/user/requests/work_order/future_service_report.dart'; import 'package:test_sa/views/pages/user/requests/work_order/work_orders_list_page.dart'; -import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; -import 'package:test_sa/views/widgets/buttons/app_button.dart'; -import 'package:test_sa/views/widgets/buttons/app_icon_button.dart'; import 'package:test_sa/views/widgets/images/files_list.dart'; import 'package:test_sa/views/widgets/loaders/app_loading.dart'; -import 'package:test_sa/views/widgets/requests/info_row.dart'; import 'package:test_sa/views/widgets/requests/request_status.dart'; import 'package:test_sa/views/widgets/sound/sound_player.dart'; -import 'package:test_sa/views/widgets/titles/app_sub_title.dart'; -import '../../../../models/service_request/search_work_order.dart'; + +import 'first_action_bottom_sheet.dart'; class ServiceRequestDetailsPage extends StatelessWidget { static const String id = "/call-details"; @@ -176,8 +164,17 @@ class ServiceRequestDetailsPage extends StatelessWidget { ), ], ).paddingOnly(bottom: 16, start: 16, end: 16)) - .onPress(() { + .onPress(() async { // todo 'sikander' add comment bottom sheet + // await showModalBottomSheet( + // context: context, + // useSafeArea: true, + // isScrollControlled: true, + // backgroundColor: Colors.transparent, + // builder: (context) => FirstActionBottomSheet( + // onPressed: () {}, + // ), + // ); }), ], ), @@ -185,44 +182,56 @@ class ServiceRequestDetailsPage extends StatelessWidget { } return Scaffold( - appBar: DefaultAppBar(title: context.translation.serviceDetails), - backgroundColor: const Color(0xfff8f9fb), - body: SafeArea( - child: FutureBuilder( - future: _serviceRequestsProvider.getServiceRequestObjectById(requestId: serviceRequest.id), - builder: (context, snap) { - if (snap.connectionState == ConnectionState.waiting) { - return const ALoading(); - } else if (snap.hasData) { - serviceRequest = snap.data; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: informationCard(), - ).expanded, - if (_userProvider.user.type != UsersTypes.engineer || (serviceRequest.statusValue == 5 || serviceRequest.statusValue == 3)) - (serviceRequest.firstAction == null - ? AppFilledButton( - label: context.translation.firstAction, - maxWidth: true, - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => CreateServiceRequestPage(serviceRequest: serviceRequest)), - ); - }) - : AppFilledButton( - label: context.translation.viewWorkOrder, - maxWidth: true, - onPressed: () { - Navigator.of(context).pushNamed(WorkOrderListPage.id, arguments: serviceRequest); - })) - .paddingOnly(start: 16, end: 16, bottom: 24), - ], - ); - } - return const SizedBox(); - }))); + appBar: DefaultAppBar(title: context.translation.serviceDetails), + backgroundColor: const Color(0xfff8f9fb), + body: SafeArea( + child: FutureBuilder( + future: _serviceRequestsProvider.getServiceRequestObjectById(requestId: serviceRequest.id), + builder: (context, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const ALoading(); + } else if (snap.hasData) { + serviceRequest = snap.data; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: informationCard(), + ).expanded, + if (_userProvider.user.type != UsersTypes.engineer || (serviceRequest.statusValue == 5 || serviceRequest.statusValue == 3)) + (serviceRequest.firstAction == null + ? AppFilledButton( + label: context.translation.firstAction, + maxWidth: true, + onPressed: () async { + // Navigator.of(context).push( + // MaterialPageRoute(builder: (_) => CreateServiceRequestPage(serviceRequest: serviceRequest)), + // ); + await showModalBottomSheet( + context: context, + useSafeArea: true, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) => FirstActionBottomSheet(request: serviceRequest), + ); + }, + ) + : AppFilledButton( + label: context.translation.viewWorkOrder, + maxWidth: true, + onPressed: () { + Navigator.of(context).pushNamed(WorkOrderListPage.id, arguments: serviceRequest); + }, + )) + .paddingOnly(start: 16, end: 16, bottom: 24), + ], + ); + } + return const SizedBox(); + }, + ), + ), + ); } } diff --git a/lib/views/widgets/date_and_time/date_picker.dart b/lib/views/widgets/date_and_time/date_picker.dart index a546235e..df0e981d 100644 --- a/lib/views/widgets/date_and_time/date_picker.dart +++ b/lib/views/widgets/date_and_time/date_picker.dart @@ -32,7 +32,7 @@ class ADatePicker extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ label.tinyFont(context), - (date?.toIso8601String() ?? context.translation.pickDate).bodyText(context).custom(color: AppColor.neutral50), + ((date?.toIso8601String()?.split("T")?.first) ?? context.translation.pickADate).bodyText(context).custom(color: AppColor.neutral50), ], ).expanded, 16.width, diff --git a/lib/views/widgets/requests/service_request_update_dialog.dart b/lib/views/widgets/requests/service_request_update_dialog.dart index df047691..4ce99376 100644 --- a/lib/views/widgets/requests/service_request_update_dialog.dart +++ b/lib/views/widgets/requests/service_request_update_dialog.dart @@ -3,14 +3,12 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/api_routes/http_status_manger.dart'; -import 'package:test_sa/controllers/localization/localization.dart'; import 'package:test_sa/controllers/providers/api/service_requests_provider.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/service_request/service_request.dart'; -import 'package:test_sa/models/subtitle.dart'; import 'package:test_sa/views/app_style/sizing.dart'; import 'package:test_sa/views/widgets/buttons/app_small_button.dart'; import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; @@ -55,7 +53,7 @@ class _ServiceRequestsUpdateDialogState extends State(context, listen: false); _settingProvider = Provider.of(context, listen: false); _serviceRequestsProvider = Provider.of(context, listen: false);