From 0f4a409780ada6c346fac6fd2242aa29971a84cf Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 1 Feb 2026 17:58:08 +0300 Subject: [PATCH] assign to me added ion gas refill and tasks --- lib/controllers/api_routes/urls.dart | 5 ++ .../providers/api/gas_refill_provider.dart | 33 +++++++- .../gas_refill/gas_refill_details.dart | 55 ++++++++++-- .../tasks/task_request_detail_view.dart | 55 +++++++++--- .../tasks/update_task_request_view.dart | 2 +- .../task_request_provider.dart | 30 +++++++ lib/views/pages/user/profile_page.dart | 3 +- .../rejection_reason_bottom_sheet.dart | 84 +++++++++++++++++++ lib/views/widgets/loaders/app_loading.dart | 6 +- 9 files changed, 251 insertions(+), 22 deletions(-) create mode 100644 lib/views/widgets/bottom_sheets/rejection_reason_bottom_sheet.dart diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 6fe56728..e09e42a1 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -172,6 +172,9 @@ class URLs { static get updateTaskByEngineerUrl => '$_baseUrl/TaskJobs/UpdateByEngineer'; + static get taskJobsAssignEngineerToHimself => "$_baseUrl/TaskJobs/AssignEngineerToHimself"; // get + static get taskJobsEngineerReject => "$_baseUrl/TaskJobs/EngineerReject"; // get + // Task LookUps static get taskJobTypeOfAlert => "$_baseUrl/Lookups/GetLookup?lookupEnum=1260"; @@ -293,6 +296,8 @@ class URLs { static get getGasRefill => "$_baseUrl/GazRefill/GetGazRefills"; // get // static get getGasRefillById => "$_baseUrl/GazRefill/GetGazRefillById"; // get older endpoint.. static get getGasRefillById => "$_baseUrl/GasRefill/GetGasRefillById"; // get + static get gasRefillAssignEngineerToHimself => "$_baseUrl/GasRefill/AssignEngineerToHimself"; // get + static get gasRefillEngineerReject => "$_baseUrl/GasRefill/EngineerReject"; // get static get getGazRefillComments => "$_baseUrl/GazRefill/GetHistoryComments"; // get static get addGazRefillComment => "$_baseUrl/GazRefill/AddHistoryComment"; // add static get updateGasRefillByEngineer => "$_baseUrl/GasRefill/UpdateGasRefillByEngineer"; // update diff --git a/lib/controllers/providers/api/gas_refill_provider.dart b/lib/controllers/providers/api/gas_refill_provider.dart index e1c0525b..3762eb38 100644 --- a/lib/controllers/providers/api/gas_refill_provider.dart +++ b/lib/controllers/providers/api/gas_refill_provider.dart @@ -48,7 +48,10 @@ class GasRefillProvider extends ChangeNotifier { GasRefillModel? gasRefillModel; - Future getGasRefillObjectById(num id) async { + Future getGasRefillObjectById(num id, {bool refresh = false}) async { + if (refresh) { + return gasRefillModel; + } try { gasRefillModel = null; Response response = await ApiManager.instance.get(URLs.getGasRefillById + "?gasRefillId=$id"); @@ -64,6 +67,34 @@ class GasRefillProvider extends ChangeNotifier { } } + Future assignEngineerToHimself(num id) async { + try { + Response response = await ApiManager.instance.post(URLs.gasRefillAssignEngineerToHimself, body: {"gasRefillId": id}); + if (response.statusCode >= 200 && response.statusCode < 300) { + gasRefillModel = GasRefillModel.fromJson(json.decode(response.body)["data"]); + return gasRefillModel; + } else { + return null; + } + } catch (error) { + return null; + } + } + + Future engineerReject(Map body) async { + try { + Response response = await ApiManager.instance.post(URLs.gasRefillEngineerReject, body: body); + if (response.statusCode >= 200 && response.statusCode < 300) { + gasRefillModel = GasRefillModel.fromJson(json.decode(response.body)["data"]); + return gasRefillModel; + } else { + return null; + } + } catch (error) { + return null; + } + } + /// return -2 if request in progress /// return -1 if error happen when sending request /// return state code if request complete may be 200, 404 or 403 diff --git a/lib/modules/tm_module/gas_refill/gas_refill_details.dart b/lib/modules/tm_module/gas_refill/gas_refill_details.dart index c785a6a1..0b2a7090 100644 --- a/lib/modules/tm_module/gas_refill/gas_refill_details.dart +++ b/lib/modules/tm_module/gas_refill/gas_refill_details.dart @@ -10,9 +10,11 @@ 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/widget_extensions.dart'; +import 'package:test_sa/helper/utils.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/modules/cx_module/chat/chat_widget.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; +import 'package:test_sa/views/widgets/bottom_sheets/rejection_reason_bottom_sheet.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/loaders/no_data_found.dart'; @@ -43,6 +45,7 @@ class _GasRefillDetailsPageState extends State { final GlobalKey _scaffoldKey = GlobalKey(); List _attachments = []; + bool isDataUpdated = false; @override void initState() { @@ -80,14 +83,14 @@ class _GasRefillDetailsPageState extends State { ), key: _scaffoldKey, body: FutureBuilder( - future: gasRefillProvider.getGasRefillObjectById(widget.model.id!), + future: gasRefillProvider.getGasRefillObjectById(widget.model.id!, refresh: isDataUpdated), builder: (context, snap) { if (snap.connectionState == ConnectionState.waiting) { return const ALoading(); } else if (snap.hasData) { _model = snap.data as GasRefillModel; _attachments = _model.gasRefillAttachments?.map((e) => File(e.attachmentName ?? '')).toList() ?? []; - + isDataUpdated = false; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -95,15 +98,55 @@ class _GasRefillDetailsPageState extends State { padding: const EdgeInsets.all(16), child: informationCard(_model), ).expanded, - if (_userProvider.user!.type == UsersTypes.engineer && (_model.status!.value! != 2)) ...[ + if (context.settingProvider.isUserFlowMedical && _userProvider.user!.type == UsersTypes.engineer && (_model.status!.value! == 0)) FooterActionButton.footerContainer( context: context, child: AppFilledButton( onPressed: () async { - await Navigator.of(context).push(MaterialPageRoute(builder: (_) => UpdateGasRefillRequest(gasRefillModel: _model))); - // getVisitData(); + Utils.showLoading(context); + await gasRefillProvider.assignEngineerToHimself(_model.id!.toInt()); + isDataUpdated = true; + Utils.hideLoading(context); + setState(() {}); }, - label: context.translation.updateRequest, + label: 'Assign To Me', + ), + ) + else if (_userProvider.user!.type == UsersTypes.engineer && (_model.status!.value! == 1)) ...[ + FooterActionButton.footerContainer( + context: context, + child: Row( + children: [ + AppFilledButton( + label: context.translation.cancel, + buttonColor: AppColor.white60, + textColor: AppColor.black10, + loading: false, + onPressed: () async { + context.showBottomSheet( + RejectionReasonBottomSheet( + onRejectPressed: (int reasonId, String comment) async { + await Future.delayed(const Duration(milliseconds: 500)); + var data = await gasRefillProvider.engineerReject({"gasRefillId": _model.id!.toInt(), "rejectReasonId": reasonId, "rejectComment": comment}); + isDataUpdated = data != null; + return isDataUpdated; + }, + onRefreshed: () { + setState(() {}); + }, + ), + title: "Rejection Reason".addTranslation); + }, + ).expanded, + 16.width, + AppFilledButton( + onPressed: () async { + await Navigator.of(context).push(MaterialPageRoute(builder: (_) => UpdateGasRefillRequest(gasRefillModel: _model))); + // getVisitData(); + }, + label: context.translation.updateRequest, + ).expanded, + ], ), ) ] diff --git a/lib/modules/tm_module/tasks/task_request_detail_view.dart b/lib/modules/tm_module/tasks/task_request_detail_view.dart index 1a60c0b9..c135ea8d 100644 --- a/lib/modules/tm_module/tasks/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks/task_request_detail_view.dart @@ -7,6 +7,7 @@ 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/helper/utils.dart'; import 'package:test_sa/models/all_requests_and_count_model.dart'; import 'package:test_sa/models/new_models/task_request/task_request_model.dart'; import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; @@ -15,6 +16,7 @@ import 'package:test_sa/modules/cx_module/chat/chat_widget.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; import 'package:test_sa/new_views/common_widgets/working_time_tile.dart'; import 'package:test_sa/providers/task_request_provider/task_request_provider.dart'; +import 'package:test_sa/views/widgets/bottom_sheets/rejection_reason_bottom_sheet.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/request_status.dart'; @@ -175,21 +177,54 @@ class _TaskRequestDetailsViewState extends State { ], ).toShadowContainer(context).paddingAll(16), ).expanded, - if (userProvider!.user!.type == UsersTypes.engineer && (taskModel?.taskJobStatus?.value != 4 && taskModel?.taskJobStatus?.value != 3)) + if (context.settingProvider.isUserFlowMedical && userProvider!.user!.type == UsersTypes.engineer && (taskModel?.taskJobStatus?.value == 0)) FooterActionButton.footerContainer( context: context, child: AppFilledButton( onPressed: () async { - if (taskProvider.taskRequestModel?.taskType?.isInstallation == true) { - taskProvider.getSiteData(siteId: taskProvider.taskRequestModel?.asset?.siteId); - } - Navigator.of(context).push(MaterialPageRoute( - builder: (_) => UpdateTaskRequest( - taskId: widget.taskId, - createdDate: widget.requestDetails?.date, - ))); + Utils.showLoading(context); + await taskProvider.assignEngineerToHimself(widget.taskId); + Utils.hideLoading(context); }, - label: context.translation.updateRequest, + label: 'Assign To Me', + ), + ) + else if (userProvider!.user!.type == UsersTypes.engineer && taskModel?.taskJobStatus?.value == 5) + ...[] + else if (userProvider!.user!.type == UsersTypes.engineer && (taskModel?.taskJobStatus?.value != 4 && taskModel?.taskJobStatus?.value != 3)) + FooterActionButton.footerContainer( + context: context, + child: Row( + children: [ + AppFilledButton( + label: context.translation.cancel, + buttonColor: AppColor.white60, + textColor: AppColor.black10, + loading: false, + onPressed: () async { + context.showBottomSheet(RejectionReasonBottomSheet( + onRejectPressed: (int reasonId, String comment) async { + await Future.delayed(const Duration(milliseconds: 500)); + return await taskProvider.engineerReject({"taskJobId": widget.taskId, "rejectReasonId": reasonId, "rejectComment": comment}); + }, + ), title: "Rejection Reason".addTranslation); + }, + ).expanded, + 16.width, + AppFilledButton( + onPressed: () async { + if (taskProvider.taskRequestModel?.taskType?.isInstallation == true) { + taskProvider.getSiteData(siteId: taskProvider.taskRequestModel?.asset?.siteId); + } + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => UpdateTaskRequest( + taskId: widget.taskId, + createdDate: widget.requestDetails?.date, + ))); + }, + label: context.translation.updateRequest, + ).expanded, + ], ), ) ]); diff --git a/lib/modules/tm_module/tasks/update_task_request_view.dart b/lib/modules/tm_module/tasks/update_task_request_view.dart index 20b6148e..a0b0028e 100644 --- a/lib/modules/tm_module/tasks/update_task_request_view.dart +++ b/lib/modules/tm_module/tasks/update_task_request_view.dart @@ -271,7 +271,7 @@ class _UpdateTaskRequestState extends State { taskModel?.timerModelList?.forEach((timer) { int durationInSecond = timer.endAt!.difference(timer.startAt!).inSeconds; taskModel.taskJobActivityEngineerTimers?.add( - TaskJobActivityEngineerTimer ( + TaskJobActivityEngineerTimer( id: 0, startDate: timer.startAt!.toIso8601String(), endDate: timer.endAt?.toIso8601String(), totalWorkingHour: ((durationInSecond) / 60 / 60), comment: timer.comments ?? comments), ); }); diff --git a/lib/providers/task_request_provider/task_request_provider.dart b/lib/providers/task_request_provider/task_request_provider.dart index 1a517d73..8e733e69 100644 --- a/lib/providers/task_request_provider/task_request_provider.dart +++ b/lib/providers/task_request_provider/task_request_provider.dart @@ -149,4 +149,34 @@ class TaskRequestProvider extends ChangeNotifier { } notifyListeners(); } + + Future assignEngineerToHimself(num id) async { + try { + Response response = await ApiManager.instance.post(URLs.taskJobsAssignEngineerToHimself, body: {"gasRefillId": id}); + if (response.statusCode >= 200 && response.statusCode < 300) { + taskRequestModel = TaskData.fromJson(json.decode(response.body)["data"]); + notifyListeners(); + return taskRequestModel; + } else { + return null; + } + } catch (error) { + return null; + } + } + + Future engineerReject(Map body) async { + try { + Response response = await ApiManager.instance.post(URLs.taskJobsEngineerReject, body: body); + if (response.statusCode >= 200 && response.statusCode < 300) { + taskRequestModel = TaskData.fromJson(json.decode(response.body)["data"]); + notifyListeners(); + return true; + } else { + return false; + } + } catch (error) { + return false; + } + } } diff --git a/lib/views/pages/user/profile_page.dart b/lib/views/pages/user/profile_page.dart index cab6b76d..b58a9002 100644 --- a/lib/views/pages/user/profile_page.dart +++ b/lib/views/pages/user/profile_page.dart @@ -165,8 +165,9 @@ class _ProfilePageState extends State { physics: const NeverScrollableScrollPhysics(), itemCount: _user.departmentName?.length ?? 0, shrinkWrap: true, + padding: EdgeInsets.zero, itemBuilder: (context, index) { - return (_user.departmentName![index] ?? "N/A").heading5(context).paddingOnly(top: 8, bottom: 8); + return (_user.departmentName![index] ?? "N/A").bodyText(context).paddingOnly(top: 4, bottom: 4); }, ), if ((_user.clientName ?? "").isNotEmpty) ...[ diff --git a/lib/views/widgets/bottom_sheets/rejection_reason_bottom_sheet.dart b/lib/views/widgets/bottom_sheets/rejection_reason_bottom_sheet.dart new file mode 100644 index 00000000..0e3c0f5e --- /dev/null +++ b/lib/views/widgets/bottom_sheets/rejection_reason_bottom_sheet.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/helper/utils.dart'; +import 'package:test_sa/models/lookup.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/app_text_form_field.dart'; +import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; +import 'package:test_sa/providers/service_request_providers/reject_reason_provider.dart'; + +class RejectionReasonBottomSheet extends StatelessWidget { + final Future Function(int reasonId, String comment) onRejectPressed; + final VoidCallback? onRefreshed; + + RejectionReasonBottomSheet({Key? key, this.onRefreshed, required this.onRejectPressed}) : super(key: key); + + @override + Widget build(BuildContext context) { + TextEditingController _commentController = TextEditingController(); + final GlobalKey _formKey = GlobalKey(); + + Lookup? selectedReason; + + return Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + SingleItemDropDownMenu( + context: context, + title: context.translation.reason, + backgroundColor: AppColor.fieldBgColor(context), + showShadow: false, + showAsBottomSheet: true, + validator: (value) { + if (value == null) return "Mandatory"; + return null; + }, + onSelect: (value) { + selectedReason = value; + }, + ), + 12.height, + AppTextFormField( + backgroundColor: AppColor.fieldBgColor(context), + labelText: context.translation.comments, + controller: _commentController, + textInputType: TextInputType.multiline, + alignLabelWithHint: true, + showSpeechToText: true, + showShadow: false, + style: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff3B3D4A)), + labelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff767676)), + floatingLabelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff3B3D4A)), + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + ), + 16.height, + AppFilledButton( + label: context.translation.reject, + buttonColor: AppColor.red30, + onPressed: () async { + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + Utils.showLoading(context); + bool isSuccess = await onRejectPressed(selectedReason!.id!, _commentController.text); + Utils.hideLoading(context); + if (isSuccess) { + Navigator.of(context).pop(); + onRefreshed?.call(); + } + } + }, + ), + 16.height, + ], + ), + ); + } +} diff --git a/lib/views/widgets/loaders/app_loading.dart b/lib/views/widgets/loaders/app_loading.dart index 9b427a7d..2aff9421 100644 --- a/lib/views/widgets/loaders/app_loading.dart +++ b/lib/views/widgets/loaders/app_loading.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; class ALoading extends StatelessWidget { const ALoading({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return const Center( - child: CircularProgressIndicator(), - ); + return const CircularProgressIndicator(color: AppColor.primary10).center; } }