From 0a9dd86ce0e4e8015f587c71554cbc2faa0e2eee Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 7 Dec 2025 15:22:53 +0300 Subject: [PATCH 01/35] improvements. --- lib/modules/cx_module/chat/helper/chat_file_viewer.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/modules/cx_module/chat/helper/chat_file_viewer.dart b/lib/modules/cx_module/chat/helper/chat_file_viewer.dart index fbd53eec..04fba4a1 100644 --- a/lib/modules/cx_module/chat/helper/chat_file_viewer.dart +++ b/lib/modules/cx_module/chat/helper/chat_file_viewer.dart @@ -127,8 +127,9 @@ class ChatFileViewer extends StatelessWidget { Future downloadFile() async { try { + // print("downloadUrl:$downloadUrl"); return await ChatApiClient() - .downloadFileWithHttp(downloadUrl, fileName: fileTypeResponse.fileName, fileTypeDescription: fileTypeResponse.fileTypeDescription, fileSource: fileTypeResponse.fileTypeId!); + .downloadFileWithHttp(downloadUrl, fileName: fileTypeResponse.fileName, fileTypeDescription: fileTypeResponse.fileTypeDescription ?? "", fileSource: fileTypeResponse.fileTypeId!); } catch (ex) { print("downloadFile:$ex"); return null; From f0c003e6fab3539ffdac58cb0491d61f352b3d3e Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 7 Dec 2025 16:27:43 +0300 Subject: [PATCH 02/35] disabled loan type dropdown added. --- .../pages/create_loan_request_page.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/modules/loan_module/pages/create_loan_request_page.dart b/lib/modules/loan_module/pages/create_loan_request_page.dart index 9590a9f4..c07f7ca2 100644 --- a/lib/modules/loan_module/pages/create_loan_request_page.dart +++ b/lib/modules/loan_module/pages/create_loan_request_page.dart @@ -70,11 +70,14 @@ class _CreateLoanRequestPageState extends State with Tick bool get isBackUpRequest => widget.workOrderId != null; + List loanTypes = []; + @override void initState() { super.initState(); Provider.of(context, listen: false).reset(); Provider.of(context, listen: false).reset(); + loanTypes = [Lookup(id: 0, name: "Standard", value: 1), Lookup(id: 1, name: "Backup", value: 2)]; } @override @@ -92,6 +95,18 @@ class _CreateLoanRequestPageState extends State with Tick crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ + SingleItemDropDownMenu( + context: context, + title: "Loan Type", + enabled: false, + staticData: loanTypes, + initialValue: isBackUpRequest ? loanTypes[1] : loanTypes[0], + showShadow: false, + backgroundColor: AppColor.fieldBgColor(context), + showAsBottomSheet: true, + onSelect: (value) {}, + ), + 16.height, if (isBackUpRequest) ...[ scanAssetButton(), 16.height, From 30bf847e6ee1290824bff4956de6ff21db204ac4 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 8 Dec 2025 09:46:23 +0300 Subject: [PATCH 03/35] department dropdown return first index item fixed. --- .../loan_module/models/medical_department_model.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/modules/loan_module/models/medical_department_model.dart b/lib/modules/loan_module/models/medical_department_model.dart index 08a6b200..1b0bbcb9 100644 --- a/lib/modules/loan_module/models/medical_department_model.dart +++ b/lib/modules/loan_module/models/medical_department_model.dart @@ -1,18 +1,20 @@ import 'package:test_sa/models/base.dart'; class MedicalDepartmentModel extends Base { - MedicalDepartmentModel({ - this.departmentName, this.departmentCode, this.ntCode, this.costCenterNumber, this.costCenterName, this.name, this.id, this.createdBy, this.createdDate, this.modifiedBy, this.modifiedDate - }) : super(identifier: id?.toString() ?? '', name: departmentName); // Handle potential null id + MedicalDepartmentModel( + {this.departmentName, this.departmentCode, this.ntCode, this.costCenterNumber, this.costCenterName, this.name, this.id, this.createdBy, this.createdDate, this.modifiedBy, this.modifiedDate}) + : super(identifier: id?.toString() ?? '', name: departmentName); // Handle potential null id MedicalDepartmentModel.fromJson(Map json) { + id = json['id']; + identifier = id?.toString() ?? ''; + name = json['departmentName'] ?? json['name']; departmentName = json['departmentName'] ?? json['name']; departmentCode = json['departmentCode']; ntCode = json['ntCode']; costCenterNumber = json['costCenterNumber']; costCenterName = json['costCenterName']; name = json['name']; - id = json['id']; createdBy = json['createdBy']; createdDate = json['createdDate']; modifiedBy = json['modifiedBy']; @@ -30,6 +32,4 @@ class MedicalDepartmentModel extends Base { String? createdDate; String? modifiedBy; String? modifiedDate; - - } From a9362d90f518a1a709678609f35a006aeebd8936 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 8 Dec 2025 10:34:34 +0300 Subject: [PATCH 04/35] asset information auto fetch from CM workOrder. --- lib/modules/cm_module/cm_detail_page.dart | 3 +- .../service_request_detail_view.dart | 2 - .../pages/create_loan_request_page.dart | 63 +++++++++---------- 3 files changed, 33 insertions(+), 35 deletions(-) diff --git a/lib/modules/cm_module/cm_detail_page.dart b/lib/modules/cm_module/cm_detail_page.dart index cbf729ca..d396e598 100644 --- a/lib/modules/cm_module/cm_detail_page.dart +++ b/lib/modules/cm_module/cm_detail_page.dart @@ -153,7 +153,8 @@ class _CMDetailPageState extends State { ], ), ).onPress(() { - Navigator.push(context, MaterialPageRoute(builder: (context) => CreateLoanRequestPage(workOrderId: widget.requestId))); + CMDetailProvider provider = Provider.of(context, listen: false); + Navigator.push(context, MaterialPageRoute(builder: (context) => CreateLoanRequestPage(workOrderId: widget.requestId, workOrder: provider.currentWorkOrder!.data))); }); } return const SizedBox(); diff --git a/lib/modules/cm_module/views/components/service_request_detail_view.dart b/lib/modules/cm_module/views/components/service_request_detail_view.dart index 6aae9abf..f8c4f320 100644 --- a/lib/modules/cm_module/views/components/service_request_detail_view.dart +++ b/lib/modules/cm_module/views/components/service_request_detail_view.dart @@ -199,8 +199,6 @@ class _ServiceRequestDetailViewState extends State { '${context.translation.assetName}: ${workOrder.assetNdModel!.name?.cleanupWhitespace?.capitalizeFirstOfEach}', style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), ), - - // 8.height, Text( '${context.translation.assetNumber}: ${workOrder.asset!.assetNumber}', style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), diff --git a/lib/modules/loan_module/pages/create_loan_request_page.dart b/lib/modules/loan_module/pages/create_loan_request_page.dart index c07f7ca2..bdd03dd7 100644 --- a/lib/modules/loan_module/pages/create_loan_request_page.dart +++ b/lib/modules/loan_module/pages/create_loan_request_page.dart @@ -16,14 +16,9 @@ import 'package:test_sa/models/device/asset.dart'; import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/generic_attachment_model.dart'; import 'package:test_sa/models/lookup.dart'; -import 'package:test_sa/models/new_models/building.dart'; -import 'package:test_sa/models/new_models/floor.dart'; -import 'package:test_sa/models/new_models/mapped_sites.dart'; -import 'package:test_sa/models/new_models/room_model.dart'; import 'package:test_sa/models/new_models/site.dart'; -import 'package:test_sa/models/new_models/task_request/task_request_model.dart'; import 'package:test_sa/models/new_models/task_request/task_type_model.dart'; -import 'package:test_sa/models/service_request/pending_service_request_model.dart'; +import 'package:test_sa/models/new_models/work_order_detail_model.dart'; import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/modules/loan_module/models/loan_form_model.dart'; @@ -39,20 +34,15 @@ import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart import 'package:test_sa/providers/department_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/task_request_provider/task_job_provider.dart'; -import 'package:test_sa/providers/task_request_provider/task_request_provider.dart'; -import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; -import 'package:test_sa/views/widgets/equipment/asset_picker.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; - import '../../../../../../new_views/common_widgets/default_app_bar.dart'; -import '../../../models/new_models/department.dart'; class CreateLoanRequestPage extends StatefulWidget { static const String id = "/create-loan"; int? workOrderId; + WorkOrderData? workOrder; - CreateLoanRequestPage({Key? key, this.workOrderId}) : super(key: key); + CreateLoanRequestPage({Key? key, this.workOrderId, this.workOrder}) : super(key: key); @override _CreateLoanRequestPageState createState() => _CreateLoanRequestPageState(); @@ -62,7 +52,6 @@ class _CreateLoanRequestPageState extends State with Tick final List attachments = []; final GlobalKey _formKey = GlobalKey(); final GlobalKey _scaffoldKey = GlobalKey(); - Asset? device; TaskType? selectedType; final LoanFormModel _loanFormModel = LoanFormModel(); @@ -108,7 +97,28 @@ class _CreateLoanRequestPageState extends State with Tick ), 16.height, if (isBackUpRequest) ...[ - scanAssetButton(), + 'Asset Information'.addTranslation.bodyText(context).custom(color: AppColor.black10), + 8.height, + Text( + '${context.translation.assetName}: ${widget.workOrder!.assetNdModel!.name?.cleanupWhitespace?.capitalizeFirstOfEach}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.assetNumber}: ${widget.workOrder!.asset!.assetNumber}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.serialNo}: ${widget.workOrder!.asset!.assetSerialNo}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.model}: ${widget.workOrder!.model?.name!.cleanupWhitespace?.capitalizeFirstOfEach}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.manufacture}: ${widget.workOrder!.manufacturer?.name?.cleanupWhitespace?.capitalizeFirstOfEach}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), 16.height, ], // ...contactInfoSection(), @@ -488,24 +498,8 @@ class _CreateLoanRequestPageState extends State with Tick ]; } - Widget scanAssetButton() { - return AssetPicker( - device: device, - showLoading: false, - borderColor: AppColor.black20, - onPick: (asset) async { - device = asset; - setState(() {}); - }, - ); - } - Future _submit() async { FocusScope.of(context).unfocus(); - if (isBackUpRequest && device == null) { - "Please scan or pick asset".showToast; - return; - } if (_formKey.currentState!.validate()) { _formKey.currentState!.save(); @@ -522,7 +516,12 @@ class _CreateLoanRequestPageState extends State with Tick body["requestorUserID"] = context.userProvider.user!.userID; if (isBackUpRequest) { - body["assetId"] = device!.id; + body["assetId"] = widget.workOrder!.asset!.id; + body["assetName"] = widget.workOrder!.assetNdModel!.name; + body["assetNumber"] = widget.workOrder!.asset!.assetNumber; + body["assetSerialNumber"] = widget.workOrder!.asset!.assetSerialNo; + body["model"] = widget.workOrder!.model?.name; + body["manufacturer"] = widget.workOrder!.manufacturer?.name; body["cMWOItemId"] = widget.workOrderId; } bool isSuccess = await loanProvider.addLoanRequest(body); From 5ff9fc58085229d2f779f927b32bb0d334a6bddf Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Tue, 9 Dec 2025 16:57:05 +0300 Subject: [PATCH 05/35] chat added gas refill, transfer asset internal & external, task request. --- .../firebase_notification_manger.dart | 3 +- lib/models/device/device_transfer.dart | 15 +++ lib/models/new_models/gas_refill_model.dart | 52 +++++++++ .../tasks_wo/task_request_detail_view.dart | 26 ++++- .../tasks_wo/task_request_item_view.dart | 4 +- .../land_page/requests/device_item_view.dart | 8 +- .../requests/gas_refill_item_view.dart | 2 + .../task_request_provider.dart | 2 +- .../device_transfer_details.dart | 35 +++++- .../user/gas_refill/gas_refill_details.dart | 34 +++--- .../widgets/gas_refill/gas_refill_list.dart | 100 +++++++++--------- 11 files changed, 203 insertions(+), 78 deletions(-) diff --git a/lib/controllers/notification/firebase_notification_manger.dart b/lib/controllers/notification/firebase_notification_manger.dart index 7a8a67f9..77f0a55c 100644 --- a/lib/controllers/notification/firebase_notification_manger.dart +++ b/lib/controllers/notification/firebase_notification_manger.dart @@ -110,12 +110,13 @@ class FirebaseNotificationManger { serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString())); break; case "7": - serviceClass = DeviceTransferDetails(model: DeviceTransfer(id: int.parse(messageData["requestNumber"].toString()))); + serviceClass = DeviceTransferDetails(model: DeviceTransfer(id: int.parse(messageData["requestNumber"].toString())), moduleId: 3); break; case "9": serviceClass = GasRefillDetailsPage( priority: messageData["priority"], date: messageData["createdOn"], + moduleId: 2, model: GasRefillModel(id: int.parse(messageData["requestNumber"].toString())), ); break; diff --git a/lib/models/device/device_transfer.dart b/lib/models/device/device_transfer.dart index c8620eaf..67b7d465 100644 --- a/lib/models/device/device_transfer.dart +++ b/lib/models/device/device_transfer.dart @@ -38,6 +38,7 @@ class DeviceTransfer { this.senderEngSignature, this.senderAttachments, this.receiverAssignedEmployeeId, + this.receiverAssignedEmployeeNumber, this.receiverMachineStatusId, this.senderMachineStatusValue, this.receiverComment, @@ -74,6 +75,7 @@ class DeviceTransfer { this.receiverMachineStatusValue, this.receiverMachineStatusName, this.senderAssignedEmployeeName, + this.senderAssignedEmployeeNumber, this.senderBuildingName, this.senderDepartmentName, this.senderEngSignatureUrl, @@ -144,6 +146,7 @@ class DeviceTransfer { }); } receiverAssignedEmployeeId = json['receiverAssignedEmployeeId']; + receiverAssignedEmployeeNumber = json['receiverAssignedEmployeeNumber']; receiverMachineStatusId = json['receiverMachineStatusId']; receiverMachineStatusValue = json['receiverMachineStatusValue']; receiverComment = json['receiverComment']; @@ -220,6 +223,7 @@ class DeviceTransfer { receiverEngSignatureUrl = json['receiverEngSignatureUrl']; receiverMachineStatusName = json['receiverMachineStatusName']; senderAssignedEmployeeName = json['senderAssignedEmployeeName']; + senderAssignedEmployeeNumber = json['senderAssignedEmployeeNumber']; senderBuildingName = json['senderBuildingName']; senderDepartmentName = json['senderDepartmentName']; senderEngSignatureUrl = json['senderEngSignatureUrl']; @@ -270,6 +274,7 @@ class DeviceTransfer { List? attachments; List? assetTransferAttachments; String? receiverAssignedEmployeeId; + String? receiverAssignedEmployeeNumber; num? receiverMachineStatusId; int? receiverMachineStatusValue; String? receiverComment; @@ -290,6 +295,7 @@ class DeviceTransfer { String? senderFloorName; String? senderDepartmentName; String? senderAssignedEmployeeName; + String? senderAssignedEmployeeNumber; String? senderMachineStatusName; String? senderEngSignatureUrl; String? receiverAssignedEmployeeName; @@ -345,6 +351,7 @@ class DeviceTransfer { String? senderEngSignature, List? senderAttachments, String? receiverAssignedEmployeeId, + String? receiverAssignedEmployeeNumber, num? receiverMachineStatusId, String? receiverComment, String? receiverStartDate, @@ -364,6 +371,7 @@ class DeviceTransfer { String? senderFloorName, String? senderDepartmentName, String? senderAssignedEmployeeName, + String? senderAssignedEmployeeNumber, String? senderMachineStatusName, String? senderEngSignatureUrl, String? receiverAssignedEmployeeName, @@ -420,6 +428,7 @@ class DeviceTransfer { senderEngSignature: senderEngSignature ?? this.senderEngSignature, senderAttachments: senderAttachments ?? this.senderAttachments, receiverAssignedEmployeeId: receiverAssignedEmployeeId ?? this.receiverAssignedEmployeeId, + receiverAssignedEmployeeNumber: receiverAssignedEmployeeNumber ?? this.receiverAssignedEmployeeNumber, receiverMachineStatusId: receiverMachineStatusId ?? this.receiverMachineStatusId, receiverComment: receiverComment ?? this.receiverComment, receiverStartDate: receiverStartDate ?? this.receiverStartDate, @@ -439,6 +448,7 @@ class DeviceTransfer { senderFloorName: senderFloorName ?? this.senderFloorName, senderDepartmentName: senderDepartmentName ?? this.senderDepartmentName, senderAssignedEmployeeName: senderAssignedEmployeeName ?? this.senderAssignedEmployeeName, + senderAssignedEmployeeNumber: senderAssignedEmployeeNumber ?? this.senderAssignedEmployeeNumber, senderMachineStatusName: senderMachineStatusName ?? this.senderMachineStatusName, senderMachineStatusValue: senderMachineStatusValue ?? this.senderMachineStatusValue, senderEngSignatureUrl: senderEngSignatureUrl ?? this.senderEngSignatureUrl, @@ -503,6 +513,7 @@ class DeviceTransfer { map['senderAttachments'] = senderAttachments!.map((v) => v.toJson()).toList(); } map['receiverAssignedEmployeeId'] = receiverAssignedEmployeeId; + map['receiverAssignedEmployeeNumber'] = receiverAssignedEmployeeNumber; map['receiverMachineStatusId'] = receiverMachineStatusId; map['receiverComment'] = receiverComment; map['receiverStartDate'] = receiverStartDate; @@ -524,6 +535,7 @@ class DeviceTransfer { map["senderFloorName"] = senderFloorName; map["senderDepartmentName"] = senderDepartmentName; map["senderAssignedEmployeeName"] = senderAssignedEmployeeName; + map["senderAssignedEmployeeNumber"] = senderAssignedEmployeeNumber; map["senderMachineStatusName"] = senderMachineStatusName; map["senderEngSignatureUrl"] = senderEngSignatureUrl; map["receiverAssignedEmployeeName"] = receiverAssignedEmployeeName; @@ -631,6 +643,7 @@ class DeviceTransfer { map['assetTransferReceiverTimers'] = receiverVisitTimers!.map((v) => v.toJson()).toList(); } map['receiverAssignedEmployeeId'] = receiverAssignedEmployeeId; + map['receiverAssignedEmployeeNumber'] = receiverAssignedEmployeeNumber; map['receiverMachineStatusId'] = receiverMachineStatusId; map['receiverComment'] = receiverComment; map['receiverStartDate'] = receiverStartDate; @@ -674,6 +687,7 @@ class DeviceTransfer { senderEngSignature = assetTransfer.senderEngSignature; senderAttachments = assetTransfer.senderAttachments; receiverAssignedEmployeeId = assetTransfer.receiverAssignedEmployeeId; + receiverAssignedEmployeeNumber = assetTransfer.receiverAssignedEmployeeNumber; receiverMachineStatusId = assetTransfer.receiverMachineStatusId; receiverComment = assetTransfer.receiverComment; receiverStartDate = assetTransfer.receiverStartDate; @@ -693,6 +707,7 @@ class DeviceTransfer { senderFloorName = assetTransfer.senderFloorName; senderDepartmentName = assetTransfer.senderDepartmentName; senderAssignedEmployeeName = assetTransfer.senderAssignedEmployeeName; + senderAssignedEmployeeNumber = assetTransfer.senderAssignedEmployeeNumber; senderMachineStatusName = assetTransfer.senderMachineStatusName; senderEngSignatureUrl = assetTransfer.senderEngSignatureUrl; receiverAssignedEmployeeName = assetTransfer.receiverAssignedEmployeeName; diff --git a/lib/models/new_models/gas_refill_model.dart b/lib/models/new_models/gas_refill_model.dart index e9e873f3..a50373df 100644 --- a/lib/models/new_models/gas_refill_model.dart +++ b/lib/models/new_models/gas_refill_model.dart @@ -523,3 +523,55 @@ class GasRefillContactPerson { }; } } +class AssignedEmployee { + String? id; + String? name; + String? email; + String? employeeId; + int? languageId; + String? extensionNo; + String? phoneNumber; + String? positionName; + String? position; + bool? isActive; + + AssignedEmployee( + {this.id, + this.name, + this.email, + this.employeeId, + this.languageId, + this.extensionNo, + this.phoneNumber, + this.positionName, + this.position, + this.isActive}); + + AssignedEmployee.fromJson(Map json) { + id = json['id']; + name = json['name']; + email = json['email']; + employeeId = json['employeeId']; + languageId = json['languageId']; + extensionNo = json['extensionNo']; + phoneNumber = json['phoneNumber']; + positionName = json['positionName']; + position = json['position']; + isActive = json['isActive']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['name'] = this.name; + data['email'] = this.email; + data['employeeId'] = this.employeeId; + data['languageId'] = this.languageId; + data['extensionNo'] = this.extensionNo; + data['phoneNumber'] = this.phoneNumber; + data['positionName'] = this.positionName; + data['position'] = this.position; + data['isActive'] = this.isActive; + return data; + } +} diff --git a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart index 612992e7..de42d20a 100644 --- a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart @@ -11,6 +11,7 @@ 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/utilities/service_request_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/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'; @@ -29,8 +30,9 @@ class TaskRequestDetailsView extends StatefulWidget { static const String id = "/task-request-detail"; final int taskId; final RequestsDetails? requestDetails; + final int moduleId; - const TaskRequestDetailsView({Key? key, required this.taskId, this.requestDetails}) : super(key: key); + const TaskRequestDetailsView({Key? key, required this.taskId, this.requestDetails, required this.moduleId}) : super(key: key); @override _TaskRequestDetailsViewState createState() { @@ -65,7 +67,27 @@ class _TaskRequestDetailsViewState extends State { Widget build(BuildContext context) { userProvider ??= Provider.of(context, listen: false); return Scaffold( - appBar: DefaultAppBar(title: context.translation.taskRequest), + appBar: DefaultAppBar( + title: context.translation.taskRequest, + actions: [ + if (context.userProvider.isEngineer || context.userProvider.isNurse) + Selector( + selector: (_, myModel) => myModel.taskRequestModel, // Selects only the userName + builder: (_, _taskReqModel, __) { + if (_taskReqModel == null) return const SizedBox(); + + return ChatWidget( + moduleId: widget.moduleId, + isShow: _taskReqModel.taskJobStatus!.value! != 0, + isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, + requestId: widget.taskId.toInt(), + assigneeEmployeeNumber: _taskReqModel.assignedEngineer!.employeeId!, + myLoginUserID: context.userProvider.user!.username!, + contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId, + ); + }) + ], + ), body: Consumer(builder: (context, taskProvider, child) { TaskData? taskModel = taskProvider.taskRequestModel; return taskProvider.isLoading diff --git a/lib/modules/tm_module/tasks_wo/task_request_item_view.dart b/lib/modules/tm_module/tasks_wo/task_request_item_view.dart index 3cc6bd05..2c164df3 100644 --- a/lib/modules/tm_module/tasks_wo/task_request_item_view.dart +++ b/lib/modules/tm_module/tasks_wo/task_request_item_view.dart @@ -68,7 +68,8 @@ class TaskRequestItemView extends StatelessWidget { Navigator.of(context).push(MaterialPageRoute( builder: (_) => TaskRequestDetailsView( taskId: requestData!.id!, - requestDetails: RequestsDetails(nameOfType: requestData?.nameOfType, status: requestData?.statusName, priority: requestData?.priorityName, date: requestData?.transactionDate), + moduleId: requestData!.transactionNo!, + requestDetails: RequestsDetails(nameOfType: requestData?.nameOfType, status: requestData?.statusName, priority: requestData?.priorityName, date: requestData?.transactionDate, ), ))); }); } @@ -122,6 +123,7 @@ class TaskRequestItemView extends StatelessWidget { Navigator.of(context).push(MaterialPageRoute( builder: (_) => TaskRequestDetailsView( taskId: requestDetails!.id!, + moduleId: requestDetails!.transactionType!, requestDetails: requestDetails, ))); }); diff --git a/lib/new_views/pages/land_page/requests/device_item_view.dart b/lib/new_views/pages/land_page/requests/device_item_view.dart index bb933db3..eafcc190 100644 --- a/lib/new_views/pages/land_page/requests/device_item_view.dart +++ b/lib/new_views/pages/land_page/requests/device_item_view.dart @@ -77,7 +77,7 @@ class DeviceItemView extends StatelessWidget { ), ], ).toShadowContainer(context, withShadow: showShadow).onPress(() { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => DeviceTransferDetails(model: DeviceTransfer(id: requestData!.id!)))); + Navigator.of(context).push(MaterialPageRoute(builder: (_) => DeviceTransferDetails(model: DeviceTransfer(id: requestData!.id!), moduleId: requestData!.transactionNo!))); }); } @@ -138,7 +138,11 @@ class DeviceItemView extends StatelessWidget { ), ], ).toShadowContainer(context, withShadow: showShadow).onPress(() { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => DeviceTransferDetails(model: DeviceTransfer(id: requestDetails!.id!)))); + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => DeviceTransferDetails( + model: DeviceTransfer(id: requestDetails!.id!), + moduleId: requestDetails!.transactionType!, + ))); }); } diff --git a/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart b/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart index 397ac41c..2337089c 100644 --- a/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart +++ b/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart @@ -74,6 +74,7 @@ class GasRefillItemView extends StatelessWidget { builder: (_) => GasRefillDetailsPage( priority: requestData!.priorityName, date: requestData!.transactionDate!, + moduleId: requestData!.transactionNo!, model: GasRefillModel(id: requestData!.id), ), ), @@ -135,6 +136,7 @@ class GasRefillItemView extends StatelessWidget { builder: (_) => GasRefillDetailsPage( priority: requestDetails!.priority!, date: requestDetails!.date, + moduleId: requestDetails!.transactionType!, model: GasRefillModel(id: requestDetails!.id), ), ), diff --git a/lib/providers/task_request_provider/task_request_provider.dart b/lib/providers/task_request_provider/task_request_provider.dart index e3d38a4b..1a517d73 100644 --- a/lib/providers/task_request_provider/task_request_provider.dart +++ b/lib/providers/task_request_provider/task_request_provider.dart @@ -77,6 +77,7 @@ class TaskRequestProvider extends ChangeNotifier { Future getTaskById({required int id, bool? showLoading = true}) async { try { isLoading = showLoading ?? true; + taskRequestModel = null; notifyListeners(); final response = await ApiManager.instance.get(URLs.getTaskByIdUrl + "?taskJobId=$id"); stateCode = response.statusCode; @@ -129,7 +130,6 @@ class TaskRequestProvider extends ChangeNotifier { isLoading = false; notifyListeners(); if (stateCode == 200) { - return true; } return false; diff --git a/lib/views/pages/device_transfer/device_transfer_details.dart b/lib/views/pages/device_transfer/device_transfer_details.dart index 1f857949..30f90c96 100644 --- a/lib/views/pages/device_transfer/device_transfer_details.dart +++ b/lib/views/pages/device_transfer/device_transfer_details.dart @@ -9,6 +9,7 @@ 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/models/device/device_transfer.dart'; +import 'package:test_sa/modules/cx_module/chat/chat_widget.dart'; import 'package:test_sa/views/pages/device_transfer/update_device_transfer.dart'; import 'package:test_sa/views/widgets/images/files_list.dart'; import 'package:test_sa/views/widgets/loaders/app_loading.dart'; @@ -22,8 +23,9 @@ import '../../widgets/requests/request_status.dart'; class DeviceTransferDetails extends StatefulWidget { final DeviceTransfer model; + final int moduleId; - const DeviceTransferDetails({Key? key, required this.model}) : super(key: key); + const DeviceTransferDetails({Key? key, required this.model, required this.moduleId}) : super(key: key); @override State createState() => _DeviceTransferDetailsState(); @@ -131,6 +133,7 @@ class _DeviceTransferDetailsState extends State { ] else ...[ // sender card _buildCard( + _model!, isSender: true, site: _model?.senderSiteName?.cleanupWhitespace.capitalizeFirstOfEach ?? "", dept: _model?.senderDepartmentName?.cleanupWhitespace.capitalizeFirstOfEach ?? "", @@ -150,6 +153,7 @@ class _DeviceTransferDetailsState extends State { 8.height, // receiver card _buildCard( + _model!, isSender: false, site: _model?.destSiteName?.cleanupWhitespace.capitalizeFirstOfEach ?? "", dept: _model?.destDepartmentName?.cleanupWhitespace.capitalizeFirstOfEach ?? "", @@ -215,7 +219,17 @@ class _DeviceTransferDetailsState extends State { ], ), ], - ), + ).expanded, + if (context.userProvider.isEngineer || context.userProvider.isNurse) + ChatWidget( + moduleId: widget.moduleId, + isShow: _model!.senderMachineStatusValue! != 0, + isReadOnly: _model!.senderMachineStatusValue! != 1, + requestId: widget.model.id!.toInt(), + assigneeEmployeeNumber: _model!.senderAssignedEmployeeNumber!, + myLoginUserID: context.userProvider.user!.username!, + contactEmployeeINumber: _model!.assetTransferContactPersons!.first.employeeNumber, + ), if ((_userProvider!.user?.type == UsersTypes.engineer)) if (isCurrentEngineerEligibleForEdit && isCurrentRequestEditAble) "edit".toSvgAsset(height: 40, width: 40).onPress(() async { @@ -248,7 +262,8 @@ class _DeviceTransferDetailsState extends State { ).toShadowContainer(context); } - _buildCard({required String site, String? building, String? dept, String? floor, required String comment, required bool isSender, StatusLabel? statusLabel, String? assignTo}) { + _buildCard(DeviceTransfer _deviceTransfer, + {required String site, String? building, String? dept, String? floor, required String comment, required bool isSender, StatusLabel? statusLabel, String? assignTo}) { bool isCurrentEngineerEligibleForEdit = isSender ? _model?.senderAssignedEmployeeId == _userProvider!.user?.userID : _model?.receiverAssignedEmployeeId == _userProvider!.user?.userID; bool isCurrentRequestClosed = isSender ? ((_model?.senderMachineStatusValue == 3)) : ((_model?.receiverMachineStatusValue == 3)); @@ -265,7 +280,17 @@ class _DeviceTransferDetailsState extends State { if (statusLabel != null) 8.height, isSender ? context.translation.senderDetails.heading5(context) : context.translation.receiverDetails.heading5(context), ], - ), + ).expanded, + if (context.userProvider.isEngineer || context.userProvider.isNurse) + ChatWidget( + moduleId: widget.moduleId, + isShow: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue!) != 0, + isReadOnly: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue) != 1, + requestId: widget.model.id!.toInt(), + assigneeEmployeeNumber: isSender ? _deviceTransfer.senderAssignedEmployeeNumber! : _deviceTransfer.receiverAssignedEmployeeNumber!, + myLoginUserID: context.userProvider.user!.username!, + contactEmployeeINumber: _deviceTransfer.assetTransferContactPersons!.first.employeeNumber, + ), if ((_userProvider!.user?.type == UsersTypes.engineer)) if (isCurrentEngineerEligibleForEdit && !isCurrentRequestClosed) @@ -273,7 +298,7 @@ class _DeviceTransferDetailsState extends State { // ? (!(_model?.senderMachineStatusName?.toLowerCase()?.contains("close") ?? false) && !(_model?.senderMachineStatusName?.toLowerCase()?.contains("complete") ?? false)) // : (!(_model?.receiverMachineStatusName?.toLowerCase()?.contains("close") ?? false) && !(_model?.receiverMachineStatusName?.toLowerCase()?.contains("complete") ?? false))) // - "edit".toSvgAsset(height: 48, width: 48).onPress(() async { + "edit".toSvgAsset(height: 48, width: 48).onPress(() async { bool isReload = (await Navigator.of(context).push(MaterialPageRoute(builder: (_) => UpdateDeviceTransfer(model: _model!, isSender: isSender)))) as bool; if (isReload ?? false) { setState(() {}); diff --git a/lib/views/pages/user/gas_refill/gas_refill_details.dart b/lib/views/pages/user/gas_refill/gas_refill_details.dart index 3773c898..f620fd31 100644 --- a/lib/views/pages/user/gas_refill/gas_refill_details.dart +++ b/lib/views/pages/user/gas_refill/gas_refill_details.dart @@ -28,8 +28,9 @@ import '../../../widgets/requests/request_status.dart'; class GasRefillDetailsPage extends StatefulWidget { GasRefillModel model; String? priority, date; + final int moduleId; - GasRefillDetailsPage({Key? key, this.priority, this.date, required this.model}) : super(key: key); + GasRefillDetailsPage({Key? key, this.priority, this.date, required this.model, required this.moduleId}) : super(key: key); @override State createState() => _GasRefillDetailsPageState(); @@ -59,21 +60,22 @@ class _GasRefillDetailsPageState extends State { appBar: DefaultAppBar( title: context.translation.gasRefillDetails, actions: [ - // if (context.userProvider.isEngineer || context.userProvider.isNurse) - // Selector( - // selector: (_, myModel) => myModel.gasRefillModel, // Selects only the userName - // builder: (_, _gasRefillModel, __) { - // if (_gasRefillModel == null) return const SizedBox(); - // return ChatWidget( - // moduleId: 2, - // isShow: _gasRefillModel.status!.value! == 1, - // // isReadOnly: _gasRefillModel.status!.value! == 1, - // requestId: widget.model.id!.toInt(), - // assigneeEmployeeNumber: _gasRefillModel.assignedEmployee!.name!, - // myLoginUserID: context.userProvider.user!.username!, - // contactEmployeeINumber: _gasRefillModel.gasRefillContactPerson!.first.employeeCode!, - // ); - // }) + if (context.userProvider.isEngineer || context.userProvider.isNurse) + Selector( + selector: (_, myModel) => myModel.gasRefillModel, // Selects only the userName + builder: (_, _gasRefillModel, __) { + if (_gasRefillModel == null) return const SizedBox(); + + return ChatWidget( + moduleId: widget.moduleId, + isShow: _gasRefillModel.status!.value! != 0, + isReadOnly: _gasRefillModel.status!.value! != 1, + requestId: widget.model.id!.toInt(), + assigneeEmployeeNumber: _gasRefillModel.assignedEmployee!.employeeId!, + myLoginUserID: context.userProvider.user!.username!, + contactEmployeeINumber: _gasRefillModel.gasRefillContactPerson!.first.employeeCode!, + ); + }) ], ), key: _scaffoldKey, diff --git a/lib/views/widgets/gas_refill/gas_refill_list.dart b/lib/views/widgets/gas_refill/gas_refill_list.dart index d9022a49..10299380 100644 --- a/lib/views/widgets/gas_refill/gas_refill_list.dart +++ b/lib/views/widgets/gas_refill/gas_refill_list.dart @@ -1,50 +1,50 @@ -import 'package:flutter/material.dart'; -import 'package:test_sa/extensions/context_extension.dart'; -import 'package:test_sa/views/pages/user/gas_refill/gas_refill_details.dart'; -import 'package:test_sa/views/widgets/gas_refill/gas_refill_item.dart'; -import 'package:test_sa/views/widgets/loaders/lazy_loading.dart'; -import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; - -import '../../../models/new_models/gas_refill_model.dart'; - -class GasRefillList extends StatefulWidget { - final List items; - final bool nextPage; - final Future Function() onLazyLoad; - - const GasRefillList({Key? key,required this.items,required this.nextPage,required this.onLazyLoad}) : super(key: key); - - @override - State createState() => _GasRefillListState(); -} - -class _GasRefillListState extends State { - @override - Widget build(BuildContext context) { - if (widget.items.isEmpty) { - return NoDataFound( - message: context.translation.noServiceRequestFound, - ); - } - return LazyLoading( - nextPage: widget.nextPage, - onLazyLoad: widget.onLazyLoad, - child: ListView.builder( - //physics: const BouncingScrollPhysics(), - itemCount: widget.items.length, - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - itemBuilder: (context, itemIndex) { - return GasRefillItem( - index: itemIndex, - item: widget.items[itemIndex], - onPressed: (model) { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => GasRefillDetailsPage(model: model))).then((value) { - model = value; - setState(() {}); - }); - }, - ); - }), - ); - } -} +// import 'package:flutter/material.dart'; +// import 'package:test_sa/extensions/context_extension.dart'; +// import 'package:test_sa/views/pages/user/gas_refill/gas_refill_details.dart'; +// import 'package:test_sa/views/widgets/gas_refill/gas_refill_item.dart'; +// import 'package:test_sa/views/widgets/loaders/lazy_loading.dart'; +// import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; +// +// import '../../../models/new_models/gas_refill_model.dart'; +// +// class GasRefillList extends StatefulWidget { +// final List items; +// final bool nextPage; +// final Future Function() onLazyLoad; +// @todo delete +// const GasRefillList({Key? key,required this.items,required this.nextPage,required this.onLazyLoad}) : super(key: key); +// +// @override +// State createState() => _GasRefillListState(); +// } +// +// class _GasRefillListState extends State { +// @override +// Widget build(BuildContext context) { +// if (widget.items.isEmpty) { +// return NoDataFound( +// message: context.translation.noServiceRequestFound, +// ); +// } +// return LazyLoading( +// nextPage: widget.nextPage, +// onLazyLoad: widget.onLazyLoad, +// child: ListView.builder( +// //physics: const BouncingScrollPhysics(), +// itemCount: widget.items.length, +// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), +// itemBuilder: (context, itemIndex) { +// return GasRefillItem( +// index: itemIndex, +// item: widget.items[itemIndex], +// onPressed: (model) { +// Navigator.of(context).push(MaterialPageRoute(builder: (_) => GasRefillDetailsPage(model: model, moduleId: null,))).then((value) { +// model = value; +// setState(() {}); +// }); +// }, +// ); +// }), +// ); +// } +// } From bf2b777f8d70c792f3e7eb218f9f269695e821b2 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Tue, 9 Dec 2025 17:15:02 +0300 Subject: [PATCH 06/35] chat improvements. --- lib/modules/cx_module/chat/chat_widget.dart | 41 ++++++++++--------- .../tasks_wo/task_request_detail_view.dart | 4 +- .../device_transfer_details.dart | 4 +- .../user/gas_refill/gas_refill_details.dart | 2 +- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/lib/modules/cx_module/chat/chat_widget.dart b/lib/modules/cx_module/chat/chat_widget.dart index 9d7c8a36..4d428abb 100644 --- a/lib/modules/cx_module/chat/chat_widget.dart +++ b/lib/modules/cx_module/chat/chat_widget.dart @@ -73,25 +73,26 @@ class _ChatWidgetState extends State { @override Widget build(BuildContext context) { - return Consumer(builder: (pContext, requestProvider, _) { - return IconButton( - icon: const Icon(Icons.chat_bubble), - onPressed: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => ChatPage( - moduleId: widget.moduleId, - requestId: widget.requestId, - title: widget.title, - readOnly: widget.isReadOnly, - assigneeEmployeeNumber: widget.assigneeEmployeeNumber, - contactEmployeeINumber: widget.contactEmployeeINumber, - myLoginUserID: widget.myLoginUserID, - ))); - }, - ).toShimmer(context: context, isShow: requestProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30); - }); - ; + return widget.isShow + ? Consumer(builder: (pContext, requestProvider, _) { + return IconButton( + icon: const Icon(Icons.chat_bubble), + onPressed: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ChatPage( + moduleId: widget.moduleId, + requestId: widget.requestId, + title: widget.title, + readOnly: widget.isReadOnly, + assigneeEmployeeNumber: widget.assigneeEmployeeNumber, + contactEmployeeINumber: widget.contactEmployeeINumber, + myLoginUserID: widget.myLoginUserID, + ))); + }, + ).toShimmer(context: context, isShow: requestProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30); + }) + : const SizedBox(); } } diff --git a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart index de42d20a..330ce2f2 100644 --- a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart @@ -78,10 +78,10 @@ class _TaskRequestDetailsViewState extends State { return ChatWidget( moduleId: widget.moduleId, - isShow: _taskReqModel.taskJobStatus!.value! != 0, + isShow: _taskReqModel.taskJobStatus!.value! != 1, isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, requestId: widget.taskId.toInt(), - assigneeEmployeeNumber: _taskReqModel.assignedEngineer!.employeeId!, + assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!, myLoginUserID: context.userProvider.user!.username!, contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId, ); diff --git a/lib/views/pages/device_transfer/device_transfer_details.dart b/lib/views/pages/device_transfer/device_transfer_details.dart index 30f90c96..03b04961 100644 --- a/lib/views/pages/device_transfer/device_transfer_details.dart +++ b/lib/views/pages/device_transfer/device_transfer_details.dart @@ -223,10 +223,10 @@ class _DeviceTransferDetailsState extends State { if (context.userProvider.isEngineer || context.userProvider.isNurse) ChatWidget( moduleId: widget.moduleId, - isShow: _model!.senderMachineStatusValue! != 0, + isShow: _model!.senderMachineStatusValue! != 4, isReadOnly: _model!.senderMachineStatusValue! != 1, requestId: widget.model.id!.toInt(), - assigneeEmployeeNumber: _model!.senderAssignedEmployeeNumber!, + assigneeEmployeeNumber: _model!.senderAssignedEmployeeNumber, myLoginUserID: context.userProvider.user!.username!, contactEmployeeINumber: _model!.assetTransferContactPersons!.first.employeeNumber, ), diff --git a/lib/views/pages/user/gas_refill/gas_refill_details.dart b/lib/views/pages/user/gas_refill/gas_refill_details.dart index f620fd31..57f81d36 100644 --- a/lib/views/pages/user/gas_refill/gas_refill_details.dart +++ b/lib/views/pages/user/gas_refill/gas_refill_details.dart @@ -71,7 +71,7 @@ class _GasRefillDetailsPageState extends State { isShow: _gasRefillModel.status!.value! != 0, isReadOnly: _gasRefillModel.status!.value! != 1, requestId: widget.model.id!.toInt(), - assigneeEmployeeNumber: _gasRefillModel.assignedEmployee!.employeeId!, + assigneeEmployeeNumber: _gasRefillModel.assignedEmployee?.employeeId!, myLoginUserID: context.userProvider.user!.username!, contactEmployeeINumber: _gasRefillModel.gasRefillContactPerson!.first.employeeCode!, ); From 4763132302738231547e8bc5edf20243cebbac62 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 10 Dec 2025 09:46:48 +0300 Subject: [PATCH 07/35] attachment as listview added. --- .../pages/create_loan_request_page.dart | 1 + .../widgets/images/multi_image_picker.dart | 80 ++++++++------ .../images/multi_image_picker_item.dart | 102 +++++++++++++++++- 3 files changed, 150 insertions(+), 33 deletions(-) diff --git a/lib/modules/loan_module/pages/create_loan_request_page.dart b/lib/modules/loan_module/pages/create_loan_request_page.dart index bdd03dd7..734b39c5 100644 --- a/lib/modules/loan_module/pages/create_loan_request_page.dart +++ b/lib/modules/loan_module/pages/create_loan_request_page.dart @@ -134,6 +134,7 @@ class _CreateLoanRequestPageState extends State with Tick attachment: attachments, buttonColor: AppColor.black10, onlyImages: false, + showAsListView: true, buttonIcon: 'image-plus'.toSvgAsset(color: context.isDark ? AppColor.primary10 : AppColor.neutral120), onChange: (attachments) { attachments = attachments; diff --git a/lib/views/widgets/images/multi_image_picker.dart b/lib/views/widgets/images/multi_image_picker.dart index fbeeb9ce..7105721e 100644 --- a/lib/views/widgets/images/multi_image_picker.dart +++ b/lib/views/widgets/images/multi_image_picker.dart @@ -320,6 +320,7 @@ class AttachmentPicker extends StatefulWidget { Color? buttonColor; final Function(List)? onChange; final bool showAsGrid; + final bool showAsListView; AttachmentPicker( {Key? key, @@ -332,6 +333,7 @@ class AttachmentPicker extends StatefulWidget { this.onlyImages = false, this.onChange, this.showAsGrid = false, + this.showAsListView = false, this.buttonColor}) : super(key: key); @@ -357,29 +359,53 @@ class _AttachmentPickerState extends State { : onFilePicker), 16.height, if (widget.attachment.isNotEmpty) - Wrap( - spacing: 8.toScreenWidth, - children: List.generate( - widget.attachment.length, - (index) { - File image = File(widget.attachment[index].name!); - return MultiFilesPickerItem( - file: image, - enabled: widget.enabled, - onRemoveTap: (image) { - if (!widget.enabled) { - return; - } - widget.attachment.removeAt(index); - if (widget.onChange != null) { - widget.onChange!(widget.attachment); - } - setState(() {}); + widget.showAsListView + ? ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (cxt, index) { + File image = File(widget.attachment[index].name!); + return MultiFilesPickerItem( + file: image, + showAsListView: widget.showAsListView, + enabled: widget.enabled, + onRemoveTap: (image) { + if (!widget.enabled) { + return; + } + widget.attachment.removeAt(index); + if (widget.onChange != null) { + widget.onChange!(widget.attachment); + } + setState(() {}); + }, + ); }, - ); - }, - ), - ), + separatorBuilder: (cxt, index) => 8.height, + itemCount: widget.attachment.length) + : Wrap( + spacing: 8.toScreenWidth, + children: List.generate( + widget.attachment.length, + (index) { + File image = File(widget.attachment[index].name!); + return MultiFilesPickerItem( + file: image, + enabled: widget.enabled, + onRemoveTap: (image) { + if (!widget.enabled) { + return; + } + widget.attachment.removeAt(index); + if (widget.onChange != null) { + widget.onChange!(widget.attachment); + } + setState(() {}); + }, + ); + }, + ), + ), ], ); } @@ -402,11 +428,6 @@ class _AttachmentPickerState extends State { } void showFileSourceSheet() async { - // if (widget.attachment.length >= 5) { - // Fluttertoast.showToast(msg: context.translation.maxImagesNumberIs5); - // return; - // } - ImageSource source = (await showModalBottomSheet( context: context, shape: const RoundedRectangleBorder( @@ -475,11 +496,6 @@ class _AttachmentPickerState extends State { } onFilePicker() async { - //TODO removed on request by Backend as they don't have anyissue with large number of files - // if (widget.attachment.length >= 5) { - // Fluttertoast.showToast(msg: context.translation.maxImagesNumberIs5); - // return; - // } ImageSource? source = await showModalBottomSheet( context: context, builder: (BuildContext context) { diff --git a/lib/views/widgets/images/multi_image_picker_item.dart b/lib/views/widgets/images/multi_image_picker_item.dart index 4669440e..2a794125 100644 --- a/lib/views/widgets/images/multi_image_picker_item.dart +++ b/lib/views/widgets/images/multi_image_picker_item.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:open_file/open_file.dart'; @@ -18,8 +19,16 @@ class MultiFilesPickerItem extends StatelessWidget { final bool enabled; final Function(File) onRemoveTap; final VoidCallback? onRemove; + final bool showAsListView; - const MultiFilesPickerItem({Key? key, required this.file, required this.enabled, required this.onRemoveTap, this.onRemove}) : super(key: key); + const MultiFilesPickerItem({ + Key? key, + required this.file, + required this.enabled, + required this.onRemoveTap, + this.onRemove, + this.showAsListView = false, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -27,6 +36,97 @@ class MultiFilesPickerItem extends StatelessWidget { bool isPdf = file.path.split(".").last.toLowerCase() == "pdf"; bool isExcel = file.path.split(".").last.toLowerCase() == "xlsx"; + if (showAsListView) { + return Row( + children: [ + Container( + width: 48.toScreenWidth, + height: 48.toScreenWidth, + decoration: BoxDecoration( + color: AppColor.neutral90, + border: Border.all(width: 1, color: context.isDark ? AppColor.neutral30 : AppColor.neutral30), + image: DecorationImage( + image: getImage(isImage, isPdf, isExcel, file), + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.circular(8), + ), + ), + 8.width, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Flexible( + child: Text( + file.path.split("/").last, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: AppTextStyles.heading6.copyWith(color: AppColor.black10), + ), + ), + 8.width, + if (enabled) + InkWell( + child: "remove".toSvgAsset(width: 16), + onTap: () { + onRemoveTap(file); + if (onRemove != null) { + onRemove!(); + } + }, + ) + ], + ), + 4.height, + RichText( + text: TextSpan( + text: 'Asset Guide | ', + style: AppTextStyles.bodyText.copyWith(color: AppColor.neutral120), + children: [ + TextSpan( + text: 'View', + recognizer: TapGestureRecognizer() + ..onTap = () async { + if (isImage) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => Scaffold( + appBar: const DefaultAppBar(), + body: SafeArea( + child: InteractiveViewer( + child: _isLocalUrl(file.path) + ? Image.file(file) + : ImageLoader( + url: URLs.getFileUrl(file.path), + boxFit: BoxFit.cover, + ), + ).center, + ), + ), + ), + ); + } else if (_isLocalUrl(file.path)) { + OpenFile.open(file.path); + } else { + if (!await launchUrl(Uri.parse(URLs.getFileUrl(file.path)!), mode: LaunchMode.externalApplication)) { + Fluttertoast.showToast(msg: "UnExpected Error with file."); + throw Exception('Could not launch'); + } + } + }, + style: AppTextStyles.bodyText.copyWith(color: AppColor.primary10, decoration: TextDecoration.underline), + ), + ], + ), + ), + ], + ).expanded + ], + ); + } + return SizedBox( width: 60.toScreenWidth, height: 60.toScreenWidth, From f2ebadf1b77e5cb18130fba82e7097065d1da4f8 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 10 Dec 2025 10:53:40 +0300 Subject: [PATCH 08/35] attachment list view added in files list widget. --- .../loan_module/provider/loan_provider.dart | 6 +- lib/views/widgets/images/files_list.dart | 235 +++++++++++++----- .../widgets/images/multi_image_picker.dart | 1 + .../images/multi_image_picker_item.dart | 8 +- pubspec.lock | 8 + pubspec.yaml | 1 + 6 files changed, 189 insertions(+), 70 deletions(-) diff --git a/lib/modules/loan_module/provider/loan_provider.dart b/lib/modules/loan_module/provider/loan_provider.dart index b5cb0093..b2a3a5e3 100644 --- a/lib/modules/loan_module/provider/loan_provider.dart +++ b/lib/modules/loan_module/provider/loan_provider.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:http/http.dart'; import 'package:test_sa/controllers/api_routes/api_manager.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; @@ -8,9 +9,10 @@ import 'dart:convert'; class LoanProvider extends ChangeNotifier { Future addLoanRequest(Map body) async { try { - Response response = await ApiManager.instance.post(URLs.addLoan, body: body); + Response response = await ApiManager.instance.post(URLs.addLoan, body: body, showToast: false); if (response.statusCode >= 200 && response.statusCode < 300) { - // trafRequestDataModel = TrafRequestDataModel.fromJson(json.decode(response.body)["data"]); + String message = (jsonDecode(response.body)["data"] ?? "") + " " + (jsonDecode(response.body)["message"] ?? ""); + Fluttertoast.showToast(msg: message ?? "", toastLength: Toast.LENGTH_LONG); return true; } return false; diff --git a/lib/views/widgets/images/files_list.dart b/lib/views/widgets/images/files_list.dart index 9a24e097..3c3a120c 100644 --- a/lib/views/widgets/images/files_list.dart +++ b/lib/views/widgets/images/files_list.dart @@ -1,10 +1,15 @@ import 'dart:io'; +import 'package:ellipsized_text/ellipsized_text.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:open_file/open_file.dart'; import 'package:test_sa/controllers/api_routes/urls.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/new_views/app_style/app_color.dart'; import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; import 'package:test_sa/views/widgets/loaders/image_loader.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -12,78 +17,180 @@ import 'package:url_launcher/url_launcher.dart'; class FilesList extends StatelessWidget { final List images; final EdgeInsets? padding; + final bool showAsListView; - const FilesList({Key? key, this.images = const [], this.padding}) : super(key: key); + const FilesList({Key? key, this.images = const [], this.padding, this.showAsListView = false}) : super(key: key); @override Widget build(BuildContext context) { - return GridView.builder( - padding: const EdgeInsets.only(top: 8, bottom: 8), - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 6, mainAxisSpacing: 8, crossAxisSpacing: 8), - itemCount: images.length, - itemBuilder: (context, itemIndex) { - if (!images[itemIndex].contains(".")) { - return const Text("Bad\nFile"); - } - var isImage = images[itemIndex].split(".").last.toLowerCase() == "png" || images[itemIndex].split(".").last.toLowerCase() == "jpg" || images[itemIndex].split(".").last.toLowerCase() == "jpeg"; - var isPdf = images[itemIndex].split(".").last.toLowerCase() == "pdf"; - var isExcel = images[itemIndex].split(".").last.toLowerCase() == "xlsx"; + return showAsListView + ? ListView.separated( + shrinkWrap: true, + padding: const EdgeInsets.only(top: 8), + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (cxt, itemIndex) { + if (!images[itemIndex].contains(".")) { + return const Text("Bad\nFile"); + } + var isImage = + images[itemIndex].split(".").last.toLowerCase() == "png" || images[itemIndex].split(".").last.toLowerCase() == "jpg" || images[itemIndex].split(".").last.toLowerCase() == "jpeg"; + var isPdf = images[itemIndex].split(".").last.toLowerCase() == "pdf"; + var isExcel = images[itemIndex].split(".").last.toLowerCase() == "xlsx"; - return Container( - decoration: ShapeDecoration( - color: Colors.white, - shape: RoundedRectangleBorder( - side: const BorderSide(width: 1, color: Color(0xFFEAF1F4)), - borderRadius: BorderRadius.circular(8), - ), - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(8), - child: (isImage - ? (_isLocalUrl(images[itemIndex]) - ? Image.file(File(images[itemIndex])) - : ImageLoader( - url: images[itemIndex], - boxFit: BoxFit.cover, - height: 48, - width: 48, - )) - : Image.asset("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png")), - ), - ).onPress(() async { - if (isImage) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => Scaffold( - body: SafeArea( - child: Stack( - children: [ - // InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center, - InteractiveViewer(child: getImageWidget(itemIndex)).center, - const ABackButton(), - ], + return Row( + children: [ + Container( + width: 48.toScreenWidth, + height: 48.toScreenWidth, + decoration: ShapeDecoration( + color: Colors.white, + shape: RoundedRectangleBorder( + side: const BorderSide(width: 1, color: Color(0xFFEAF1F4)), + borderRadius: BorderRadius.circular(8), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: (isImage + ? (_isLocalUrl(images[itemIndex]) + ? Image.file(File(images[itemIndex])) + : ImageLoader( + url: images[itemIndex], + boxFit: BoxFit.cover, + height: 48, + width: 48, + )) + : Image.asset("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png")), ), ), - ), - ), - ); - } else if (_isLocalUrl(images[itemIndex])) { - OpenFile.open(images[itemIndex]); - } else { - try { - if (!await launchUrl(Uri.parse(images[itemIndex]), mode: LaunchMode.externalApplication)) { - Fluttertoast.showToast(msg: "UnExpected Error with file."); - throw Exception('Could not launch '); + 8.width, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + EllipsizedText( + images[itemIndex].split("/").last, + style: AppTextStyles.heading6.copyWith(color: AppColor.black10), + type: EllipsisType.start, // or EllipsisType.middle + ), + 4.height, + RichText( + text: TextSpan( + text: 'Asset Guide | ', + style: AppTextStyles.bodyText.copyWith(color: AppColor.neutral120), + children: [ + TextSpan( + text: 'View', + recognizer: TapGestureRecognizer() + ..onTap = () async { + if (isImage) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => Scaffold( + body: SafeArea( + child: Stack( + children: [ + // InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center, + InteractiveViewer(child: getImageWidget(itemIndex)).center, + const ABackButton(), + ], + ), + ), + ), + ), + ); + } else if (_isLocalUrl(images[itemIndex])) { + OpenFile.open(images[itemIndex]); + } else { + try { + if (!await launchUrl(Uri.parse(images[itemIndex]), mode: LaunchMode.externalApplication)) { + Fluttertoast.showToast(msg: "UnExpected Error with file."); + throw Exception('Could not launch '); + } + } catch (ex) { + Fluttertoast.showToast(msg: "No Viewer found."); + } + } + }, + style: AppTextStyles.bodyText.copyWith(color: AppColor.primary10, decoration: TextDecoration.underline), + ), + ], + ), + ), + ], + ).expanded + ], + ); + }, + separatorBuilder: (cxt, index) => 8.height, + itemCount: images.length) + : GridView.builder( + padding: const EdgeInsets.only(top: 8, bottom: 8), + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 6, mainAxisSpacing: 8, crossAxisSpacing: 8), + itemCount: images.length, + itemBuilder: (context, itemIndex) { + if (!images[itemIndex].contains(".")) { + return const Text("Bad\nFile"); } - } catch (ex) { - Fluttertoast.showToast(msg: "No Viewer found."); - } - } - }); - }, - ); + var isImage = + images[itemIndex].split(".").last.toLowerCase() == "png" || images[itemIndex].split(".").last.toLowerCase() == "jpg" || images[itemIndex].split(".").last.toLowerCase() == "jpeg"; + var isPdf = images[itemIndex].split(".").last.toLowerCase() == "pdf"; + var isExcel = images[itemIndex].split(".").last.toLowerCase() == "xlsx"; + + return Container( + decoration: ShapeDecoration( + color: Colors.white, + shape: RoundedRectangleBorder( + side: const BorderSide(width: 1, color: Color(0xFFEAF1F4)), + borderRadius: BorderRadius.circular(8), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: (isImage + ? (_isLocalUrl(images[itemIndex]) + ? Image.file(File(images[itemIndex])) + : ImageLoader( + url: images[itemIndex], + boxFit: BoxFit.cover, + height: 48, + width: 48, + )) + : Image.asset("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png")), + ), + ).onPress(() async { + if (isImage) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => Scaffold( + body: SafeArea( + child: Stack( + children: [ + // InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center, + InteractiveViewer(child: getImageWidget(itemIndex)).center, + const ABackButton(), + ], + ), + ), + ), + ), + ); + } else if (_isLocalUrl(images[itemIndex])) { + OpenFile.open(images[itemIndex]); + } else { + try { + if (!await launchUrl(Uri.parse(images[itemIndex]), mode: LaunchMode.externalApplication)) { + Fluttertoast.showToast(msg: "UnExpected Error with file."); + throw Exception('Could not launch '); + } + } catch (ex) { + Fluttertoast.showToast(msg: "No Viewer found."); + } + } + }); + }, + ); // return ListView.separated( // padding: padding ?? EdgeInsets.symmetric(horizontal: 32 * AppStyle.getScaleFactor(context)), diff --git a/lib/views/widgets/images/multi_image_picker.dart b/lib/views/widgets/images/multi_image_picker.dart index 7105721e..e2973118 100644 --- a/lib/views/widgets/images/multi_image_picker.dart +++ b/lib/views/widgets/images/multi_image_picker.dart @@ -363,6 +363,7 @@ class _AttachmentPickerState extends State { ? ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, itemBuilder: (cxt, index) { File image = File(widget.attachment[index].name!); return MultiFilesPickerItem( diff --git a/lib/views/widgets/images/multi_image_picker_item.dart b/lib/views/widgets/images/multi_image_picker_item.dart index 2a794125..662e7e4e 100644 --- a/lib/views/widgets/images/multi_image_picker_item.dart +++ b/lib/views/widgets/images/multi_image_picker_item.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:ellipsized_text/ellipsized_text.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -59,11 +60,10 @@ class MultiFilesPickerItem extends StatelessWidget { Row( children: [ Flexible( - child: Text( + child: EllipsizedText( file.path.split("/").last, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: AppTextStyles.heading6.copyWith(color: AppColor.black10), + style: AppTextStyles.bodyText.copyWith(color: AppColor.black10), + type: EllipsisType.start, // or EllipsisType.middle ), ), 8.width, diff --git a/pubspec.lock b/pubspec.lock index 44813421..f09bc410 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -265,6 +265,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + ellipsized_text: + dependency: "direct main" + description: + name: ellipsized_text + sha256: d4d372c2baf8d4adfb2ca3728c1e5955d1683e75fae774b311d557ad13317105 + url: "https://pub.dev" + source: hosted + version: "1.2.0" fake_async: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a7689979..35ecdeaa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -98,6 +98,7 @@ dependencies: tree_view_flutter: ^1.0.2 audio_waveforms: ^1.3.0 signalr_netcore: ^1.4.4 + ellipsized_text: ^1.2.0 local_auth_darwin: any dev_dependencies: From 4b991d807c0c7870a1905b24b0f4f13416af5e37 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 14 Dec 2025 11:06:15 +0300 Subject: [PATCH 09/35] v1.4.0+31 uploaded on stores. --- ios/Runner.xcodeproj/project.pbxproj | 6 ------ lib/controllers/api_routes/urls.dart | 10 +++++----- pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 6d04a2a4..edbf0bd9 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -469,8 +469,6 @@ "-framework", "\"nanopb\"", "-framework", - "\"qr_code_scanner\"", - "-framework", "\"speech_to_text\"", "-framework", "\"sqflite\"", @@ -689,8 +687,6 @@ "-framework", "\"nanopb\"", "-framework", - "\"qr_code_scanner\"", - "-framework", "\"speech_to_text\"", "-framework", "\"sqflite\"", @@ -795,8 +791,6 @@ "-framework", "\"nanopb\"", "-framework", - "\"qr_code_scanner\"", - "-framework", "\"speech_to_text\"", "-framework", "\"sqflite\"", diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 5454b406..82e9fb9c 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -1,16 +1,16 @@ class URLs { URLs._(); - static const String appReleaseBuildNumber = "28"; + static const String appReleaseBuildNumber = "31"; - // static const host1 = "https://atomsm.hmg.com"; // production url + static const host1 = "https://atomsm.hmg.com"; // production url // static const host1 = "https://atomsmdev.hmg.com"; // local DEV url - static const host1 = "https://atomsmuat.hmg.com"; // local UAT url + // static const host1 = "https://atomsmuat.hmg.com"; // local UAT url - static final String _baseUrl = "$_host/mobile"; // host local UAT + // static final String _baseUrl = "$_host/mobile"; // host local UAT // static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis // static final String _baseUrl = "$_host/v4/mobile"; // for asset inventory on UAT - // static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM + static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM,asset inventory // static final String _baseUrl = "$_host/v5/mobile"; // v5 for data segregation static String _host = host1; diff --git a/pubspec.yaml b/pubspec.yaml index e7584098..ba5987b6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.3.11+30 +version: 1.4.0+31 environment: sdk: ">=3.5.0 <4.0.0" From e1efd59252032b97e0db60ed2be468f1d38584f7 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 15 Dec 2025 12:24:37 +0300 Subject: [PATCH 10/35] request no, md and site details, remove item if no finding selected and dashboard api added for quality user. --- lib/controllers/api_routes/urls.dart | 2 + lib/dashboard_latest/dashboard_provider.dart | 9 +- .../create_equipment_internal_audit_form.dart | 16 +++- .../equipment_internal_audit_detail_page.dart | 44 ++++++++-- .../equipment_internal_audit_item_view.dart | 6 +- .../system_internal_audit_detail_page.dart | 88 +++++++++++++++++-- .../system_internal_audit_item_view.dart | 9 +- 7 files changed, 152 insertions(+), 22 deletions(-) diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 6e9b4c37..d35b5ae9 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -58,8 +58,10 @@ class URLs { //service request new flow urls. static get nurseDashboardCountUrl => '$_baseUrl/ServiceRequest/GetDashboardNurseCount'; + static get qaAuditorCount => '$_baseUrl/ServiceRequest/GetDashboardQAAuditorCount'; static get nurseDashboardDetailsUrl => '$_baseUrl/ServiceRequest/GetDashboardNurseDetails'; + static get qaAuditorDetails => '$_baseUrl/ServiceRequest/GetDashboardQAAuditorDetails'; static get nurseRejectUrl => '$_baseUrl/ServiceRequest/NurseReject'; diff --git a/lib/dashboard_latest/dashboard_provider.dart b/lib/dashboard_latest/dashboard_provider.dart index c09cb621..8853589a 100644 --- a/lib/dashboard_latest/dashboard_provider.dart +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -106,7 +106,9 @@ class DashBoardProvider extends ChangeNotifier { isAllCountLoading = true; notifyListeners(); String url = ''; - if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { + if (usersType == UsersTypes.qualityUser) { + url = URLs.qaAuditorCount; + } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { url = URLs.engineerDashboardCountUrl; } else { url = URLs.nurseDashboardCountUrl; @@ -241,7 +243,10 @@ class DashBoardProvider extends ChangeNotifier { Response response; String url = ''; - if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { + + if (usersType == UsersTypes.qualityUser) { + url = URLs.qaAuditorDetails; + } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { //need to check pagination for not assigned task @waseem. //these checks are to call different apis for dashboard for engineer... diff --git a/lib/modules/internal_audit_module/pages/equipment_internal_audit/create_equipment_internal_audit_form.dart b/lib/modules/internal_audit_module/pages/equipment_internal_audit/create_equipment_internal_audit_form.dart index 532a5b14..060a866a 100644 --- a/lib/modules/internal_audit_module/pages/equipment_internal_audit/create_equipment_internal_audit_form.dart +++ b/lib/modules/internal_audit_module/pages/equipment_internal_audit/create_equipment_internal_audit_form.dart @@ -42,6 +42,8 @@ class _CreateEquipmentInternalAuditFormState extends State _scaffoldKey = GlobalKey(); FocusNode auditCheckListNode = FocusNode(); + int noFindingValue = 4; + @override void initState() { super.initState(); @@ -78,7 +80,6 @@ class _CreateEquipmentInternalAuditFormState extends State( context: context, showAsBottomSheet: true, @@ -89,6 +90,15 @@ class _CreateEquipmentInternalAuditFormState extends State element.value == noFindingValue); + } catch (ex) {} + if (noFindingElement != null) { + value!.removeWhere((element) => element.value != noFindingElement!.value); + } + } _equipmentinternalAuditModel.findings = value ?? []; }, ), @@ -137,9 +147,9 @@ class _CreateEquipmentInternalAuditFormState extends State const AppLazyLoading()); _equipmentinternalAuditModel.auditorId = context.userProvider.user?.userID; - bool status = await internalAuditProvider.addEquipmentInternalAudit(context: context, request: _equipmentinternalAuditModel); + bool status = await internalAuditProvider.addEquipmentInternalAudit(context: context, request: _equipmentinternalAuditModel); Navigator.pop(context); - if(status){ + if (status) { Navigator.pop(context); } } diff --git a/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart b/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart index 84999bcb..796a401d 100644 --- a/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart +++ b/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart @@ -19,6 +19,7 @@ import 'package:test_sa/new_views/common_widgets/default_app_bar.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'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; class EquipmentInternalAuditDetailPage extends StatefulWidget { static const String id = "/details-internal-audit"; @@ -51,12 +52,10 @@ class _EquipmentInternalAuditDetailPageState extends State getAuditData() async { model = await _internalAuditProvider.getEquipmentInternalAuditById(widget.auditId); allAttachments.clear(); - allAttachments = [ + allAttachments = [ ...(model?.attachments ?? []), ...(model?.engineerData?.attachments ?? []), ]; - - } @override @@ -82,12 +81,43 @@ class _EquipmentInternalAuditDetailPageState extends State Date: Mon, 15 Dec 2025 14:55:57 +0300 Subject: [PATCH 11/35] merge conflict fixed --- .../tm_module/device_transfer/device_transfer_details.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/modules/tm_module/device_transfer/device_transfer_details.dart b/lib/modules/tm_module/device_transfer/device_transfer_details.dart index 8bb50dc3..e249bd35 100644 --- a/lib/modules/tm_module/device_transfer/device_transfer_details.dart +++ b/lib/modules/tm_module/device_transfer/device_transfer_details.dart @@ -10,7 +10,6 @@ import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/device/device_transfer.dart'; import 'package:test_sa/modules/cx_module/chat/chat_widget.dart'; -import 'package:test_sa/views/pages/device_transfer/update_device_transfer.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/loading_manager.dart'; From db2175b7f511a1035962c0308e27a39a83221e8a Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 15 Dec 2025 16:02:24 +0300 Subject: [PATCH 12/35] quality auditor role added. --- lib/models/user.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/models/user.dart b/lib/models/user.dart index 76ac1a2c..6a5abeee 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -110,6 +110,8 @@ class User { return UsersTypes.assessorTl; case "R-3": // Quality User return UsersTypes.qualityUser; + case "R-58": // Quality User + return UsersTypes.qualityUser; default: return null; } From 17210195db07dc51bf3101466cceea9abbc6632d Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 18 Dec 2025 12:20:30 +0300 Subject: [PATCH 13/35] uat improvements. md->department, enable recall and alert for quality user. --- lib/controllers/providers/api/all_requests_provider.dart | 4 ++-- lib/dashboard_latest/dashboard_provider.dart | 7 +++++++ lib/dashboard_latest/widgets/progress_fragment.dart | 9 ++++++++- .../models/equipment_internal_audit_data_model.dart | 8 ++++++++ .../equipment_internal_audit_detail_page.dart | 2 +- .../equipment_internal_audit_item_view.dart | 3 +-- .../system_internal_audit_item_view.dart | 2 +- .../land_page/my_request/all_requests_filter_page.dart | 4 +++- .../pages/land_page/my_request/my_requests_page.dart | 3 +-- 9 files changed, 32 insertions(+), 10 deletions(-) diff --git a/lib/controllers/providers/api/all_requests_provider.dart b/lib/controllers/providers/api/all_requests_provider.dart index a382b967..73dc09b6 100644 --- a/lib/controllers/providers/api/all_requests_provider.dart +++ b/lib/controllers/providers/api/all_requests_provider.dart @@ -139,8 +139,8 @@ class AllRequestsProvider extends ChangeNotifier { return list; } if (context.userProvider.isQualityUser) { - - list = [10,11]; + list = [10, 11]; + if (context.settingProvider.isUserFlowMedical) list.insert(0, 7); return list; } diff --git a/lib/dashboard_latest/dashboard_provider.dart b/lib/dashboard_latest/dashboard_provider.dart index 8853589a..f1a92f54 100644 --- a/lib/dashboard_latest/dashboard_provider.dart +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -377,6 +377,13 @@ class CategoryTabs { return tabs; } + if (userType == UsersTypes.qualityUser) { + tabs.add(CategoryTabs(label: 'Open', tag: 1, id: 2)); + tabs.add(CategoryTabs(label: 'In Progress', tag: 2, id: 3)); + tabs.add(CategoryTabs(label: 'Closed', tag: 3, id: 4)); + return tabs; + } + tabs.add(CategoryTabs(label: 'Open', tag: 1, id: 2)); tabs.add(CategoryTabs(label: 'In Progress', tag: 2, id: 3)); tabs.add(CategoryTabs(label: 'Pending Acknowledgement', tag: 3, id: 1)); diff --git a/lib/dashboard_latest/widgets/progress_fragment.dart b/lib/dashboard_latest/widgets/progress_fragment.dart index a56f54e6..4ab32aab 100644 --- a/lib/dashboard_latest/widgets/progress_fragment.dart +++ b/lib/dashboard_latest/widgets/progress_fragment.dart @@ -182,6 +182,13 @@ class ProgressFragment extends StatelessWidget { ChartData('Open', snapshot.dashboardCount?.data?.countOpen?.toDouble() ?? 0.0, AppColor.blueStatus(context)), ChartData('In Progress', snapshot.dashboardCount?.data?.countInprogress?.toDouble() ?? 0.0, AppColor.orange70), ]; + } else if (_userProvider.isQualityUser) { + total = (snapshot.dashboardCount?.data?.countComplete ?? 0) + (snapshot.dashboardCount?.data?.countInprogress ?? 0) + (snapshot.dashboardCount?.data?.countOpen ?? 0); + chartData = [ + ChartData('Closed', snapshot.dashboardCount?.data?.countComplete?.toDouble() ?? 0.0, AppColor.green70), + ChartData('Open', snapshot.dashboardCount?.data?.countOpen?.toDouble() ?? 0.0, AppColor.blueStatus(context)), + ChartData('In Progress', snapshot.dashboardCount?.data?.countInprogress?.toDouble() ?? 0.0, AppColor.orange70), + ]; } else { total = (snapshot.dashboardCount?.data?.countComplete ?? 0) + (snapshot.dashboardCount?.data?.countInprogress ?? 0) + (snapshot.dashboardCount?.data?.countOpen ?? 0); chartData = [ @@ -248,7 +255,7 @@ class ProgressFragment extends StatelessWidget { showChartValuesOutside: false, decimalPlaces: 1, ), - ).toShimmer(isShow: snapshot.isAllCountLoading, radius: 250,context: context).paddingAll(0).toShadowContainer(context), + ).toShimmer(isShow: snapshot.isAllCountLoading, radius: 250, context: context).paddingAll(0).toShadowContainer(context), ], ), ), diff --git a/lib/modules/internal_audit_module/models/equipment_internal_audit_data_model.dart b/lib/modules/internal_audit_module/models/equipment_internal_audit_data_model.dart index 92f41bab..1e62dd1d 100644 --- a/lib/modules/internal_audit_module/models/equipment_internal_audit_data_model.dart +++ b/lib/modules/internal_audit_module/models/equipment_internal_audit_data_model.dart @@ -170,6 +170,8 @@ class Asset { String? siteName; int? siteId; String? modelDefinition; + int? departmentId; + String? departmentName; Asset({ this.id, @@ -181,6 +183,8 @@ class Asset { this.siteName, this.siteId, this.modelDefinition, + this.departmentId, + this.departmentName, }); Asset.fromJson(Map json) { @@ -193,6 +197,8 @@ class Asset { siteName = json['siteName']; siteId = json['siteId']; modelDefinition = json['modelDefinition']; + departmentId = json['departmentId']; + departmentName = json['departmentName']; } Map toJson() { @@ -206,6 +212,8 @@ class Asset { data['siteName'] = siteName; data['siteId'] = siteId; data['modelDefinition'] = modelDefinition; + data['departmentId'] = departmentId; + data['departmentName'] = departmentName; return data; } } diff --git a/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart b/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart index 796a401d..74903c92 100644 --- a/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart +++ b/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart @@ -217,8 +217,8 @@ class _EquipmentInternalAuditDetailPageState extends State EquipmentInternalAuditDetailPage(auditId: requestDetails!.id!))); + Navigator.push(context, MaterialPageRoute(builder: (context) => EquipmentInternalAuditDetailPage(auditId: requestData!.id!))); }); } return Column( diff --git a/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart b/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart index 37b510c8..91a01b22 100644 --- a/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart +++ b/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart @@ -61,7 +61,7 @@ class SystemInternalAuditItemView extends StatelessWidget { ), ], ).toShadowContainer(context, withShadow: showShadow).onPress(() async { - Navigator.push(context, MaterialPageRoute(builder: (context) => SystemInternalAuditDetailPage(auditId: requestDetails!.id!))); + Navigator.push(context, MaterialPageRoute(builder: (context) => SystemInternalAuditDetailPage(auditId: requestData!.id!))); }); } return Column( diff --git a/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart index 677e6106..155babc9 100644 --- a/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart +++ b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart @@ -101,7 +101,9 @@ class _AllRequestsFilterPageState extends State { types = {"TRAF": 9}; } if (context.userProvider.isQualityUser) { - types = {'Equipment Internal Audit': 10, 'System Internal Audit': 11}; + if (context.settingProvider.isUserFlowMedical) types = {'Recall and Alert': 7}; + types['Equipment Internal Audit'] = 10; + types['System Internal Audit'] = 11; } final statuses = { diff --git a/lib/new_views/pages/land_page/my_request/my_requests_page.dart b/lib/new_views/pages/land_page/my_request/my_requests_page.dart index a097e209..a39cc687 100644 --- a/lib/new_views/pages/land_page/my_request/my_requests_page.dart +++ b/lib/new_views/pages/land_page/my_request/my_requests_page.dart @@ -44,11 +44,9 @@ class _MyRequestsPageState extends State { Request(2, context.translation.gasRefill), Request(3, context.translation.transferAsset), Request(4, context.translation.preventiveMaintenance), - ]; if (Provider.of(context, listen: false).user!.type != UsersTypes.normal_user) { requestsList.add(Request(5, context.translation.recurrentWo)); - } //TODO unCommit this to enable task requestsList.add(Request(6, context.translation.taskRequest)); @@ -80,6 +78,7 @@ class _MyRequestsPageState extends State { if (context.userProvider.isQualityUser) { requestsList = [ Request(null, context.translation.allWorkOrder), + if (context.settingProvider.isUserFlowMedical) Request(7, 'Recall and Alert'), Request(10, 'Equipment Internal Audit'), Request(11, 'System Internal Audit'), ]; From 2b274f1f83ec0d318636cccda074d2b3c6669596 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 21 Dec 2025 08:21:33 +0300 Subject: [PATCH 14/35] bug 1518 fixed. --- lib/modules/loan_module/pages/create_loan_request_page.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/modules/loan_module/pages/create_loan_request_page.dart b/lib/modules/loan_module/pages/create_loan_request_page.dart index 734b39c5..fe76e39b 100644 --- a/lib/modules/loan_module/pages/create_loan_request_page.dart +++ b/lib/modules/loan_module/pages/create_loan_request_page.dart @@ -121,18 +121,16 @@ class _CreateLoanRequestPageState extends State with Tick ), 16.height, ], + ...doctorInfoSection(), // ...contactInfoSection(), - // 16.height, - ...itemDetailsSection(), 16.height, - ...doctorInfoSection(), + ...itemDetailsSection(), 16.height, ...vendorDetailsSection(), 16.height, AttachmentPicker( label: context.translation.attachments, attachment: attachments, - buttonColor: AppColor.black10, onlyImages: false, showAsListView: true, buttonIcon: 'image-plus'.toSvgAsset(color: context.isDark ? AppColor.primary10 : AppColor.neutral120), From 6d20e6f30775873ab84be6feb9b112cb80a6b753 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 22 Dec 2025 11:24:05 +0300 Subject: [PATCH 15/35] chat enable for cm module when engineer assigned. --- .../firebase_notification_manger.dart | 6 +- .../views/nurse/create_new_request_view.dart | 2 +- .../service_request_detail_main_view.dart | 56 +++++++++---------- .../requests/service_request_item_view.dart | 4 +- .../create__device_transfer_request.dart | 4 -- .../requests/pending_requests_screen.dart | 5 +- 6 files changed, 34 insertions(+), 43 deletions(-) diff --git a/lib/controllers/notification/firebase_notification_manger.dart b/lib/controllers/notification/firebase_notification_manger.dart index 77f0a55c..5d3117ba 100644 --- a/lib/controllers/notification/firebase_notification_manger.dart +++ b/lib/controllers/notification/firebase_notification_manger.dart @@ -101,13 +101,13 @@ class FirebaseNotificationManger { break; //these three request are same corrective maintenance.... case "3": - serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString())); + serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); break; case "8": - serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString())); + serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); break; case "11": - serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString())); + serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); break; case "7": serviceClass = DeviceTransferDetails(model: DeviceTransfer(id: int.parse(messageData["requestNumber"].toString())), moduleId: 3); diff --git a/lib/modules/cm_module/views/nurse/create_new_request_view.dart b/lib/modules/cm_module/views/nurse/create_new_request_view.dart index c6e603dc..80e90d94 100644 --- a/lib/modules/cm_module/views/nurse/create_new_request_view.dart +++ b/lib/modules/cm_module/views/nurse/create_new_request_view.dart @@ -300,7 +300,7 @@ class _CreateNewRequestState extends State with TickerProvider bool checkPendingRequest = false; void showPendingRequests() { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => PendingServiceRequestScreen(pendingAssetServiceRequest!))); + Navigator.of(context).push(MaterialPageRoute(builder: (_) => PendingServiceRequestScreen(pendingAssetServiceRequest!, 1))); } void showPendingRequestBottomSheet() async { diff --git a/lib/modules/cm_module/views/service_request_detail_main_view.dart b/lib/modules/cm_module/views/service_request_detail_main_view.dart index 78029461..3e2fab9c 100644 --- a/lib/modules/cm_module/views/service_request_detail_main_view.dart +++ b/lib/modules/cm_module/views/service_request_detail_main_view.dart @@ -13,6 +13,7 @@ import 'package:test_sa/modules/cm_module/service_request_detail_provider.dart'; import 'package:test_sa/modules/cm_module/views/components/bottom_sheets/service_request_bottomsheet.dart'; import 'package:test_sa/modules/cx_module/chat/chat_page.dart'; import 'package:test_sa/modules/cx_module/chat/chat_provider.dart'; +import 'package:test_sa/modules/cx_module/chat/chat_widget.dart'; import 'package:test_sa/modules/cx_module/survey/survey_page.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; @@ -22,8 +23,9 @@ import 'components/service_request_detail_view.dart'; class ServiceRequestDetailMain extends StatefulWidget { final int requestId; + final int moduleId; - ServiceRequestDetailMain({Key? key, required this.requestId}) : super(key: key); + ServiceRequestDetailMain({Key? key, required this.requestId, required this.moduleId}) : super(key: key); @override _ServiceRequestDetailMainState createState() { @@ -88,37 +90,29 @@ class _ServiceRequestDetailMainState extends State { Navigator.pop(context); }, actions: [ - if (context.userProvider.isEngineer || context.userProvider.isNurse) - Selector( - selector: (_, myModel) => myModel.isLoading, // Selects only the userName - builder: (_, isLoading, __) { - if (isLoading) { - return const SizedBox(); - } else { - ServiceRequestDetailProvider provider = Provider.of(context, listen: false); - int? statusValue = provider.currentWorkOrder?.data?.status?.value; - if (statusValue == 2 || statusValue == 3 || statusValue == 5 || statusValue == 6) { - getChatToken(moduleId, provider.currentWorkOrder?.data?.workOrderNo ?? ""); - return Consumer(builder: (pContext, requestProvider, _) { - return IconButton( - icon: const Icon(Icons.chat_bubble), - onPressed: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => ChatPage( - moduleId: moduleId, - requestId: widget.requestId, - title: _requestProvider.currentWorkOrder?.data?.workOrderNo ?? "", - readOnly: _requestProvider.isReadOnlyRequest, - ))); - }, - ).toShimmer(context: context, isShow: requestProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30); - }); - } - return const SizedBox(); + Selector( + selector: (_, myModel) => myModel.isLoading, // Selects only the userName + builder: (_, isLoading, __) { + if (isLoading) { + return const SizedBox(); + } else { + ServiceRequestDetailProvider provider = Provider.of(context, listen: false); + if ((context.userProvider.isEngineer || context.userProvider.isNurse) && + provider.currentWorkOrder?.data?.assignedEmployee != null && + (provider.currentWorkOrder?.data?.workOrderContactPerson?.isNotEmpty ?? false)) { + return ChatWidget( + moduleId: widget.moduleId, + isShow: (context.userProvider.isEngineer || context.userProvider.isNurse) && provider.currentWorkOrder?.data?.assignedEmployee != null, + isReadOnly: _requestProvider.isReadOnlyRequest, + requestId: widget.requestId.toInt(), + assigneeEmployeeNumber: provider.currentWorkOrder?.data?.assignedEmployee!.employeeId!, + myLoginUserID: context.userProvider.user!.username!, + contactEmployeeINumber: provider.currentWorkOrder?.data?.workOrderContactPerson.first.employeeId, + ); } - }), + return const SizedBox(); + } + }), isNurse ? IconButton( icon: 'qr'.toSvgAsset( diff --git a/lib/new_views/pages/land_page/requests/service_request_item_view.dart b/lib/new_views/pages/land_page/requests/service_request_item_view.dart index 0fac59f4..c6b32c87 100644 --- a/lib/new_views/pages/land_page/requests/service_request_item_view.dart +++ b/lib/new_views/pages/land_page/requests/service_request_item_view.dart @@ -81,7 +81,7 @@ class ServiceRequestItemView extends StatelessWidget { ), ], ).toShadowContainer(context, withShadow: showShadow).onPress(() async { - await Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: requestData!.id!))); + await Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: requestData!.id!, moduleId: requestData!.transactionNo!))); if (refreshData) { Provider.of(context, listen: false).refreshDashboard(userType: Provider.of(context, listen: false).user!.type!, context: context); } @@ -140,7 +140,7 @@ class ServiceRequestItemView extends StatelessWidget { ), ], ).toShadowContainer(context, withShadow: showShadow).onPress(() { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: requestDetails!.id!))); + Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: requestDetails!.id!, moduleId: requestDetails!.transactionType!))); }); } diff --git a/lib/views/pages/device_transfer/create__device_transfer_request.dart b/lib/views/pages/device_transfer/create__device_transfer_request.dart index 319e9be8..861dbc9b 100644 --- a/lib/views/pages/device_transfer/create__device_transfer_request.dart +++ b/lib/views/pages/device_transfer/create__device_transfer_request.dart @@ -62,10 +62,6 @@ class _CreateDeviceTransferRequestState extends State PendingServiceRequestScreen(pendingAssetServiceRequest!))); - } - void _onSubmit() async { _transferModel.assetId = _pickedAsset?.id; _transferModel.destSiteId = _assetDestination.site?.id; diff --git a/lib/views/pages/user/requests/pending_requests_screen.dart b/lib/views/pages/user/requests/pending_requests_screen.dart index edd02de4..30b4a814 100644 --- a/lib/views/pages/user/requests/pending_requests_screen.dart +++ b/lib/views/pages/user/requests/pending_requests_screen.dart @@ -9,9 +9,10 @@ import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; import 'package:test_sa/views/widgets/sound/sound_player.dart'; class PendingServiceRequestScreen extends StatelessWidget { + final int moduleId; final PendingAssetServiceRequest pendingAssetServiceRequest; - const PendingServiceRequestScreen(this.pendingAssetServiceRequest, {Key? key}) : super(key: key); + const PendingServiceRequestScreen(this.pendingAssetServiceRequest, this.moduleId, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -63,7 +64,7 @@ class PendingServiceRequestScreen extends StatelessWidget { ], ), ).onPress(() { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: pendingAssetServiceRequest.details![index].id ?? 0))); + Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: pendingAssetServiceRequest.details![index].id ?? 0, moduleId: moduleId))); }))); } } From 6d6be40b9e4949b677868d992610996cdc079cf9 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 25 Dec 2025 11:34:41 +0300 Subject: [PATCH 16/35] chat notification redirection added. --- .../firebase_notification_manger.dart | 39 +++++++++++++++++++ lib/main.dart | 1 + .../cx_module/chat/chat_api_client.dart | 2 +- .../tasks_wo/task_request_detail_view.dart | 24 +++++++----- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/lib/controllers/notification/firebase_notification_manger.dart b/lib/controllers/notification/firebase_notification_manger.dart index 5d3117ba..cc32e159 100644 --- a/lib/controllers/notification/firebase_notification_manger.dart +++ b/lib/controllers/notification/firebase_notification_manger.dart @@ -12,6 +12,7 @@ import 'package:test_sa/modules/cm_module/views/service_request_detail_main_view import 'package:test_sa/modules/cx_module/survey/survey_page.dart'; import 'package:test_sa/modules/pm_module/ppm_wo/ppm_details_page.dart'; import 'package:test_sa/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart'; +import 'package:test_sa/modules/tm_module/tasks_wo/task_request_detail_view.dart'; import 'package:test_sa/views/pages/device_transfer/device_transfer_details.dart'; import 'package:test_sa/views/pages/user/gas_refill/gas_refill_details.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; @@ -88,6 +89,44 @@ class FirebaseNotificationManger { String? transactionType = messageData["transactionType"]?.toString(); + if (transactionType == null) { + return; + } else if (transactionType == "17" && messageData["requestType"] == "chat") { + int moduleId = int.parse(messageData["moduleId"].toString()); + int requestNumber = int.parse(messageData["requestNumber"].toString()); + + switch (moduleId) { + case 1: // cm + serviceClass = ServiceRequestDetailMain(requestId: requestNumber, moduleId: moduleId); + break; + case 2: // gas refill + serviceClass = GasRefillDetailsPage( + priority: messageData["priority"], + date: messageData["createdOn"], + moduleId: moduleId, + model: GasRefillModel(id: requestNumber), + ); + break; + case 3: //transfer + serviceClass = DeviceTransferDetails(model: DeviceTransfer(id: requestNumber), moduleId: moduleId); + break; + case 6: // task + serviceClass = TaskRequestDetailsView( + taskId: requestNumber, + moduleId: moduleId, + // requestDetails: RequestsDetails(nameOfType: requestData?.nameOfType, status: requestData?.statusName, priority: requestData?.priorityName, date: requestData?.transactionDate, ), + ); + ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); + break; + + default: + serviceClass = const Scaffold(body: Center(child: NoDataFound())); + } + + Navigator.of(context).push(MaterialPageRoute(builder: (_) => serviceClass!)); + return; + } + // PPM=1, // ServiceRequestEngineer = 3, // AssetTransfer=7, diff --git a/lib/main.dart b/lib/main.dart index 8855155a..0d138e27 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -126,6 +126,7 @@ import 'providers/service_request_providers/reject_reason_provider.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); // HttpOverrides.global = MyHttpOverrides(); // for later use. _configureLocalTimeZone(); NotificationManger.initialisation((notificationDetails) {}, (id, title, body, payload) async {}); diff --git a/lib/modules/cx_module/chat/chat_api_client.dart b/lib/modules/cx_module/chat/chat_api_client.dart index b859e305..a98cac23 100644 --- a/lib/modules/cx_module/chat/chat_api_client.dart +++ b/lib/modules/cx_module/chat/chat_api_client.dart @@ -82,7 +82,7 @@ class ChatApiClient { Future loadParticipants(int moduleId, int referenceId, String? assigneeEmployeeNumber) async { Response response = - await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/chat/context/$moduleId/$referenceId?assigneeEmployeeNumber=$assigneeEmployeeNumber", token: chatLoginResponse!.token); + await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/chat/context/$moduleId/$referenceId?assigneeEmployeeNumber=$assigneeEmployeeNumber&requestType=chat&transactionType=17", token: chatLoginResponse!.token); if (!kReleaseMode) { // logger.i("login-res: " + response.body); diff --git a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart index 330ce2f2..e4777fec 100644 --- a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart @@ -75,16 +75,20 @@ class _TaskRequestDetailsViewState extends State { selector: (_, myModel) => myModel.taskRequestModel, // Selects only the userName builder: (_, _taskReqModel, __) { if (_taskReqModel == null) return const SizedBox(); - - return ChatWidget( - moduleId: widget.moduleId, - isShow: _taskReqModel.taskJobStatus!.value! != 1, - isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, - requestId: widget.taskId.toInt(), - assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!, - myLoginUserID: context.userProvider.user!.username!, - contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId, - ); + if ((context.userProvider.isEngineer || context.userProvider.isNurse) && + _taskReqModel.assignedEngineer?.employeeId != null && + (_taskReqModel.taskJobContactPersons?.isNotEmpty ?? false)) { + return ChatWidget( + moduleId: widget.moduleId, + isShow: _taskReqModel.taskJobStatus!.value! != 1, + isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, + requestId: widget.taskId.toInt(), + assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!, + myLoginUserID: context.userProvider.user!.username!, + contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId, + ); + } else + return const SizedBox(); }) ], ), From a7c206e6f40ca1752031dedad55456bcc986066e Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 25 Dec 2025 11:36:01 +0300 Subject: [PATCH 17/35] chat notification redirection added. --- lib/modules/cx_module/chat/chat_api_client.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/modules/cx_module/chat/chat_api_client.dart b/lib/modules/cx_module/chat/chat_api_client.dart index a98cac23..29d9239a 100644 --- a/lib/modules/cx_module/chat/chat_api_client.dart +++ b/lib/modules/cx_module/chat/chat_api_client.dart @@ -81,8 +81,9 @@ class ChatApiClient { } Future loadParticipants(int moduleId, int referenceId, String? assigneeEmployeeNumber) async { - Response response = - await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/chat/context/$moduleId/$referenceId?assigneeEmployeeNumber=$assigneeEmployeeNumber&requestType=chat&transactionType=17", token: chatLoginResponse!.token); + Response response = await ApiClient().getJsonForResponse( + "${URLs.chatHubUrlApi}/chat/context/$moduleId/$referenceId?assigneeEmployeeNumber=$assigneeEmployeeNumber&requestType=chat&transactionType=17", + token: chatLoginResponse!.token); if (!kReleaseMode) { // logger.i("login-res: " + response.body); From ab6b14376342e9e0e4dbac32d7e260d9ceb6630c Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 25 Dec 2025 12:41:02 +0300 Subject: [PATCH 18/35] improvements --- lib/extensions/context_extension.dart | 4 +- .../pages/asset_delivery_page.dart | 102 ++++++------ .../pages/attachment_view.dart | 9 +- .../pages/change_status_bottomsheet.dart | 33 ++-- .../cost_center_form_view.dart | 88 +++++----- .../delivery_inspection_form_view.dart | 37 ++--- .../end_user_cost_center_list_view.dart | 36 ++-- .../technical_inspection_lines_list_view.dart | 155 +++++++++--------- 8 files changed, 229 insertions(+), 235 deletions(-) diff --git a/lib/extensions/context_extension.dart b/lib/extensions/context_extension.dart index f85af74c..96e79211 100644 --- a/lib/extensions/context_extension.dart +++ b/lib/extensions/context_extension.dart @@ -29,11 +29,9 @@ extension BuildContextExtension on BuildContext { return isTablet; } - - void showConfirmDialog(String message, {String? title, VoidCallback? onTap, String? okTitle}) => showDialog( context: this, - builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap, title: title,okTitle: okTitle), + builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap, title: title, okTitle: okTitle), ); Future showBottomSheet(Widget childWidget, {bool? isDismissible, String? title}) => showModalBottomSheet( diff --git a/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart b/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart index aad1d276..b3a3174b 100644 --- a/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart +++ b/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart @@ -74,60 +74,58 @@ class _AssetDeliveryPageState extends State { if (dataModel == null) { return const NoDataFound().center; } - return ListView( - padding: const EdgeInsets.all(12), - children: [ - requestDetailCard(context), - const Divider().defaultStyle(context), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Asset Delivery Table", - style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), - ), - Row( - children: [ - Container( - height: 24, - width: 24, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: AppColor.primary10, // or AppColor.blueStatus(context) - ), - alignment: Alignment.center, - child: const Icon( - Icons.add, - color: Colors.white, - size: 16, + return SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + requestDetailCard(context), + const Divider().defaultStyle(context), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Asset Delivery Table", + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + Row( + children: [ + Container( + height: 24, + width: 24, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: AppColor.primary10, // or AppColor.blueStatus(context) + ), + alignment: Alignment.center, + child: const Icon( + Icons.add, + color: Colors.white, + size: 16, + ), ), - ), - 4.width, - Text( - "Add more", - style: AppTextStyles.bodyText.copyWith( - color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, - decoration: TextDecoration.underline, + 4.width, + Text( + "Add more", + style: AppTextStyles.bodyText.copyWith( + color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, + decoration: TextDecoration.underline, + ), ), - ), - ], - ).onPress(() async { - await assetDeliveryProvider?.addNewDelivery(requestId: widget.requestId).then((status) async { - if (status) { - await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestId); - } - }); - }), - ], - ), - 8.height, - assetDeliveryTableList(context: context, assetDeliveryProvider: provider), - ], - ).toShadowContainer( - context, - borderRadius: 20, - padding: 0, - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + ], + ).onPress(() async { + await assetDeliveryProvider?.addNewDelivery(requestId: widget.requestId).then((status) async { + if (status) { + await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestId); + } + }); + }), + ], + ), + 8.height, + assetDeliveryTableList(context: context, assetDeliveryProvider: provider), + ], + ).toShadowContainer(context, borderRadius: 20), ); }, ), diff --git a/lib/modules/asset_delivery_module/pages/attachment_view.dart b/lib/modules/asset_delivery_module/pages/attachment_view.dart index 0c22772e..a8834fcc 100644 --- a/lib/modules/asset_delivery_module/pages/attachment_view.dart +++ b/lib/modules/asset_delivery_module/pages/attachment_view.dart @@ -78,6 +78,7 @@ class _AssetDeliveryAttachmentViewState extends State attachement = []; for (var item in _attachments) { String fileName = - CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? ''; + CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? ''; attachement.add(GenericAttachmentModel(id: item.id ?? 0, name: fileName)); } showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); - Map payLoad ={ + Map payLoad = { 'assetDeliveryExternalDeliveryId': widget.tableItemId, - 'attachments':attachement, + 'attachments': attachement, }; - await assetDeliveryProvider.saveAttachment(payload:payLoad).then((status) async { + await assetDeliveryProvider.saveAttachment(payload: payLoad).then((status) async { Navigator.pop(context); if (status) { ///Need to verify need to stay on this page and refresh or go back.. diff --git a/lib/modules/asset_delivery_module/pages/change_status_bottomsheet.dart b/lib/modules/asset_delivery_module/pages/change_status_bottomsheet.dart index 696207bf..1eb16c9c 100644 --- a/lib/modules/asset_delivery_module/pages/change_status_bottomsheet.dart +++ b/lib/modules/asset_delivery_module/pages/change_status_bottomsheet.dart @@ -1,5 +1,3 @@ - - import 'package:flutter/material.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart'; @@ -21,7 +19,7 @@ class AssetDeliveryBottomSheet { ); } - static Future changeStatusBottomSheet({required BuildContext context,required VoidCallback cancelStatusTap,required VoidCallback statusChangeTap}) { + static Future changeStatusBottomSheet({required BuildContext context, required VoidCallback cancelStatusTap, required VoidCallback statusChangeTap}) { return buildBottomSheetParent( context: context, childWidget: Column( @@ -31,14 +29,17 @@ class AssetDeliveryBottomSheet { 8.height, Align( alignment: AlignmentDirectional.centerStart, - child: Text('Change Status'.addTranslation,style: TextStyle( - fontSize: 21.toScreenWidth, - fontWeight: FontWeight.w500, - fontStyle: FontStyle.normal, - decoration: TextDecoration.none, - ),), + child: Text( + 'Change Status'.addTranslation, + style: TextStyle( + fontSize: 21.toScreenWidth, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.normal, + decoration: TextDecoration.none, + ), + ), ), - 10.height, + 16.height, Row( children: [ AppFilledButton( @@ -50,14 +51,16 @@ class AssetDeliveryBottomSheet { showBorder: true, onPressed: () async { cancelStatusTap(); - }, ).expanded, 12.width, - AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: () { - statusChangeTap(); - - }).expanded, + AppFilledButton( + buttonColor: AppColor.green70, + label: 'Change Status'.addTranslation, + maxWidth: true, + onPressed: () { + statusChangeTap(); + }).expanded, ], ), ], diff --git a/lib/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart b/lib/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart index 4dec60db..3cc0497b 100644 --- a/lib/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart @@ -1,4 +1,3 @@ - import 'dart:developer'; import 'package:flutter/material.dart'; @@ -103,32 +102,30 @@ class _CostCenterFormViewState extends State { backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: DefaultAppBar( title: 'Cost Center for Item'.addTranslation, - onBackPress: () { - Navigator.pop(context); - }, + // onBackPress: () { + // Navigator.pop(context); + // }, ), body: Consumer(builder: (context, provider, child) { - return Column( - children: [ - itemDetails(context: context, itemNo: widget.itemNo, itemDescription: widget.itemDescription), - provider.isLoading - ? const CircularProgressIndicator(color: AppColor.primary10).center - : ListView.builder( + return provider.isLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Column( + children: [ + itemDetails(context: context, itemNo: widget.itemNo, itemDescription: widget.itemDescription), + ListView.builder( itemCount: _list.length + 1, shrinkWrap: true, - padding: EdgeInsets.all(widget.cardPadding ?? 16), + padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8), itemBuilder: (context, index) { if (index == _list.length) { - return Visibility( - child: AppFilledButton( - label: "Add Cost Center".addTranslation, - maxWidth: true, - textColor: AppColor.textColor(context), - buttonColor: context.isDark ? AppColor.neutral60 : AppColor.white10, - icon: Icon(Icons.add_circle, color: AppColor.blueStatus(context)), - showIcon: true, - onPressed: _addNewEntry, - ), + return AppFilledButton( + label: "Add Cost Center".addTranslation, + maxWidth: true, + textColor: AppColor.textColor(context), + buttonColor: context.isDark ? AppColor.neutral60 : AppColor.white10, + icon: Icon(Icons.add_circle, color: AppColor.blueStatus(context)), + showIcon: true, + onPressed: _addNewEntry, ); } return CostCenterItemCard( @@ -142,28 +139,27 @@ class _CostCenterFormViewState extends State { ); }, ).expanded, - 8.height, - FooterActionButton.footerContainer( - context: context, - child: AppFilledButton( - buttonColor: AppColor.primary10, - label: 'Save'.addTranslation, - maxWidth: true, - onPressed: () async { - showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); - await assetDeliveryProvider.saveCostCenterToLineDeliveryInspection(tableItemId: widget.tableItemId, lineId: widget.lineId, list: _list).then((status) async { - Navigator.pop(context); - if (status) { - Navigator.pop(context); - assetDeliveryProvider.getDeliveryLinesListById( - itemId: widget.tableItemId, - ); - } - }); - }), - ), - ], - ); + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + buttonColor: AppColor.primary10, + label: 'Save'.addTranslation, + maxWidth: true, + onPressed: () async { + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await assetDeliveryProvider.saveCostCenterToLineDeliveryInspection(tableItemId: widget.tableItemId, lineId: widget.lineId, list: _list).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context); + assetDeliveryProvider.getDeliveryLinesListById( + itemId: widget.tableItemId, + ); + } + }); + }), + ), + ], + ); }), ); } @@ -172,6 +168,7 @@ class _CostCenterFormViewState extends State { Widget itemDetails({required BuildContext context, String? itemNo, String? itemDescription}) { return Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ Text( 'Item No'.addTranslation, @@ -197,7 +194,8 @@ Widget itemDetails({required BuildContext context, String? itemNo, String? itemD context, borderRadius: 20, padding: 12, - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + showShadow: false, + margin: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8), ); } @@ -293,6 +291,6 @@ class CostCenterItemCard extends StatelessWidget { ], ) ], - ).toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 16, vertical: 12)).paddingOnly(bottom: 12); + ).toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 12, vertical: 12)).paddingOnly(bottom: 12); } } diff --git a/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart b/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart index 40a4f61c..3ef8c0a0 100644 --- a/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart @@ -124,29 +124,28 @@ class _DeliveryInspectionFormViewState extends State ], ).toShadowContainer(context, borderRadius: 20, padding: 12)) .expanded, - if (!viewOnly) ...[ - FooterActionButton.footerContainer( - context: context, - child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Attachments'.addTranslation, maxWidth: true, onPressed: _attachmentTap), - ), - 8.height, - ], FooterActionButton.footerContainer( context: context, child: !viewOnly - ? Row( + ? Column( children: [ - AppFilledButton( - label: 'Cancel'.addTranslation, - maxWidth: true, - buttonColor: AppColor.background(context), - // textColor: context.isDark ? AppColor.neutral30 : Colors.white, - textColor: AppColor.red30, - showBorder: true, - onPressed: _cancelTap, - ).expanded, - 12.width, - AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded, + AppFilledButton(buttonColor: AppColor.primary10, label: 'Attachments'.addTranslation, maxWidth: true, onPressed: _attachmentTap), + 16.height, + Row( + children: [ + AppFilledButton( + label: 'Cancel'.addTranslation, + maxWidth: true, + buttonColor: AppColor.background(context), + // textColor: context.isDark ? AppColor.neutral30 : Colors.white, + textColor: AppColor.red30, + showBorder: true, + onPressed: _cancelTap, + ).expanded, + 12.width, + AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded, + ], + ), ], ) : AppFilledButton(buttonColor: AppColor.primary10, label: 'Next'.addTranslation, maxWidth: true, onPressed: _nextTap), diff --git a/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart b/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart index 3711f237..5accb0ae 100644 --- a/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart @@ -83,28 +83,28 @@ class _EndUserCostCenterLIstViewState extends State { children: [ //Refactor this SingleChildScrollView(padding: const EdgeInsets.all(16), child: linesList(context).toShadowContainer(context, borderRadius: 20, padding: 12)).expanded, - if (!viewOnly) ...[ - FooterActionButton.footerContainer( - context: context, - child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Attachments'.addTranslation, maxWidth: true, onPressed: _attachmentTap), - ), - 8.height, - ], + !viewOnly ? FooterActionButton.footerContainer( context: context, - child: Row( + child: Column( children: [ - AppFilledButton( - label: 'Cancel'.addTranslation, - maxWidth: true, - buttonColor: AppColor.background(context), - textColor: AppColor.red30, - showBorder: true, - onPressed: _cancelTap, - ).expanded, - 12.width, - AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded, + AppFilledButton(buttonColor: AppColor.primary10, label: 'Attachments'.addTranslation, maxWidth: true, onPressed: _attachmentTap), + 16.height, + Row( + children: [ + AppFilledButton( + label: 'Cancel'.addTranslation, + maxWidth: true, + buttonColor: AppColor.background(context), + textColor: AppColor.red30, + showBorder: true, + onPressed: _cancelTap, + ).expanded, + 12.width, + AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded, + ], + ), ], )) : const SizedBox(), diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart index 61e52c87..11105480 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart @@ -84,89 +84,86 @@ class _TechnicalInspectionLinesListViewState extends State 8.height, + itemBuilder: (context, index) { + if (index == 0) { + return Text( + "Lines", + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ); + } + + final DeliveryLineModel model = technicalInspectionLinesList[index - 1]; + return AssetDeliveryLineCard( + deliveryLineModel: model, + isViewOnly: viewOnly, + isTechnical: true, + editPress: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UpdateTechnicalInspectionLinesView( + requestModel: widget.requestModel, + deliveryTableModel: widget.deliveryTableModel, + deliveryLineModel: model, + ), + ), + ); + }, + ); + }, + ).toShadowContainer(context, borderRadius: 20, padding: 12), + ], + ), + ).expanded, + FooterActionButton.footerContainer( + context: context, + child: !viewOnly + ? Column( + children: [ + AppFilledButton(buttonColor: AppColor.primary10, label: 'Attachments'.addTranslation, maxWidth: true, onPressed: _attachmentTap), + 16.height, + Row( + children: [ + AppFilledButton( + label: 'Cancel'.addTranslation, + maxWidth: true, + buttonColor: AppColor.background(context), + // textColor: context.isDark ? AppColor.neutral30 : Colors.white, + textColor: AppColor.red30, + showBorder: true, + onPressed: _cancelTap, + ).expanded, + 12.width, + AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded, + ], + ), + ], + ) + : AppFilledButton(buttonColor: AppColor.primary10, label: 'Next'.addTranslation, maxWidth: true, onPressed: _nextTap), + ), + ], + ), ), ); } - Widget linesList(BuildContext context) { - if (isLoading) { - return SizedBox.expand( - child: const CircularProgressIndicator(color: AppColor.primary10).center, - ); - } - - if (technicalInspectionLinesList.isEmpty) { - return const NoDataFound().center; - } - - return ListView.separated( - padding: const EdgeInsets.all(16), - itemCount: technicalInspectionLinesList.length + 1, // +1 for the header - separatorBuilder: (_, __) => 8.height, - itemBuilder: (context, index) { - if (index == 0) { - return Text( - "Lines", - style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), - ); - } - - final DeliveryLineModel model = technicalInspectionLinesList[index - 1]; - return AssetDeliveryLineCard( - deliveryLineModel: model, - isViewOnly: viewOnly, - isTechnical: true, - editPress: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => UpdateTechnicalInspectionLinesView( - requestModel: widget.requestModel, - deliveryTableModel: widget.deliveryTableModel, - deliveryLineModel: model, - ), - ), - ); - }, - ); - }, - ).toShadowContainer(context, borderRadius: 20, padding: 12); - } - //May be need to show data. Widget inspectionDetails() { return Column( From 2425c78cf934578dd5cd5cb19895774adf076b84 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Sun, 28 Dec 2025 11:10:17 +0300 Subject: [PATCH 19/35] chat count implemented --- lib/modules/cx_module/chat/chat_widget.dart | 46 +++-- .../chat/model/chat_participant_model.dart | 39 +++-- .../common_widgets/custom_badge.dart | 157 +++++++++++++++++- 3 files changed, 201 insertions(+), 41 deletions(-) diff --git a/lib/modules/cx_module/chat/chat_widget.dart b/lib/modules/cx_module/chat/chat_widget.dart index 4d428abb..f84c7032 100644 --- a/lib/modules/cx_module/chat/chat_widget.dart +++ b/lib/modules/cx_module/chat/chat_widget.dart @@ -1,9 +1,12 @@ +import 'dart:developer'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/modules/cm_module/service_request_detail_provider.dart'; +import 'package:test_sa/new_views/common_widgets/custom_badge.dart'; import 'chat_page.dart'; import 'chat_provider.dart'; @@ -74,24 +77,31 @@ class _ChatWidgetState extends State { @override Widget build(BuildContext context) { return widget.isShow - ? Consumer(builder: (pContext, requestProvider, _) { - return IconButton( - icon: const Icon(Icons.chat_bubble), - onPressed: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => ChatPage( - moduleId: widget.moduleId, - requestId: widget.requestId, - title: widget.title, - readOnly: widget.isReadOnly, - assigneeEmployeeNumber: widget.assigneeEmployeeNumber, - contactEmployeeINumber: widget.contactEmployeeINumber, - myLoginUserID: widget.myLoginUserID, - ))); - }, - ).toShimmer(context: context, isShow: requestProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30); + ? Consumer(builder: (pContext, chatProvider, _) { + final int unreadCount = (!chatProvider.chatLoginTokenLoading && (chatProvider.chatParticipantModel?.unreadCount ?? 0) > 0) ? chatProvider.chatParticipantModel!.unreadCount! : 0; + return CustomBadge2( + value: unreadCount, + top: 2, + right: 2, + minSize: 18, + child: IconButton( + icon: const Icon(Icons.chat_bubble), + onPressed: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ChatPage( + moduleId: widget.moduleId, + requestId: widget.requestId, + title: widget.title, + readOnly: widget.isReadOnly, + assigneeEmployeeNumber: widget.assigneeEmployeeNumber, + contactEmployeeINumber: widget.contactEmployeeINumber, + myLoginUserID: widget.myLoginUserID, + ))); + }, + ).toShimmer(context: context, isShow: chatProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30), + ); }) : const SizedBox(); } diff --git a/lib/modules/cx_module/chat/model/chat_participant_model.dart b/lib/modules/cx_module/chat/model/chat_participant_model.dart index 6ef1d4e4..671bc4c4 100644 --- a/lib/modules/cx_module/chat/model/chat_participant_model.dart +++ b/lib/modules/cx_module/chat/model/chat_participant_model.dart @@ -1,12 +1,15 @@ +import 'dart:developer'; + class ChatParticipantModel { int? id; String? title; String? conversationType; List? participants; - String? lastMessage; + // String? lastMessage; String? createdAt; + int? unreadCount; - ChatParticipantModel({this.id, this.title, this.conversationType, this.participants, this.lastMessage, this.createdAt}); + ChatParticipantModel({this.id, this.title, this.conversationType, this.participants, this.createdAt, this.unreadCount}); ChatParticipantModel.fromJson(Map json) { id = json['id']; @@ -18,20 +21,22 @@ class ChatParticipantModel { participants!.add(new Participants.fromJson(v)); }); } - lastMessage = json['lastMessage']; + // lastMessage = json['lastMessage']; createdAt = json['createdAt']; + unreadCount = json['unreadCount']; } Map toJson() { - final Map data = new Map(); - data['id'] = this.id; - data['title'] = this.title; - data['conversationType'] = this.conversationType; - if (this.participants != null) { - data['participants'] = this.participants!.map((v) => v.toJson()).toList(); + final Map data = {}; + data['id'] = id; + data['title'] = title; + data['conversationType'] = conversationType; + if (participants != null) { + data['participants'] = participants!.map((v) => v.toJson()).toList(); } - data['lastMessage'] = this.lastMessage; - data['createdAt'] = this.createdAt; + // data['lastMessage'] = lastMessage; + data['createdAt'] = createdAt; + data['unreadCount'] = unreadCount; return data; } } @@ -54,12 +59,12 @@ class Participants { } Map toJson() { - final Map data = new Map(); - data['userId'] = this.userId; - data['userName'] = this.userName; - data['employeeNumber'] = this.employeeNumber; - data['role'] = this.role; - data['userStatus'] = this.userStatus; + final Map data = {}; + data['userId'] = userId; + data['userName'] = userName; + data['employeeNumber'] = employeeNumber; + data['role'] = role; + data['userStatus'] = userStatus; return data; } } diff --git a/lib/new_views/common_widgets/custom_badge.dart b/lib/new_views/common_widgets/custom_badge.dart index 654304ec..a048877f 100644 --- a/lib/new_views/common_widgets/custom_badge.dart +++ b/lib/new_views/common_widgets/custom_badge.dart @@ -5,12 +5,20 @@ import 'package:test_sa/new_views/app_style/app_color.dart'; class CustomBadge extends StatelessWidget { final Widget child; // The widget that the badge will be overlaid on. final int value; // The value or text to be displayed in the badge. - final Color color; // The background color of the badge. + final Color color; + final double? positionR; + final double? positionT; + final double? height; + final double? width; - const CustomBadge({ + CustomBadge({ Key? key, required this.child, required this.value, + this.height, + this.width, + this.positionR, + this.positionT, this.color = AppColor.red30, // Default color is red }) : super(key: key); @@ -22,13 +30,14 @@ class CustomBadge extends StatelessWidget { child, // The main widget // if (value > 0) Positioned( - right: -6, - top: -6, + right: positionR ?? -6, + top: positionT ?? -6, + //Need to check why opacity he used here this is not correct need to make this total dynamic and responsive child: Opacity( opacity: value > 0 ? 1 : 0, child: Container( - height: 23, - constraints: const BoxConstraints(minWidth: 23), + height: height ?? 23, + constraints: BoxConstraints(minWidth: width ?? 23), padding: const EdgeInsets.all(3), alignment: Alignment.center, decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(30)), @@ -43,3 +52,139 @@ class CustomBadge extends StatelessWidget { ); } } + +//Need to use this badge every where.. +class CustomBadge2 extends StatelessWidget { + final Widget child; + final int value; + final double top; + final double right; + final double left; + final double bottom; + final double minSize; + final Color backgroundColor; + final Color textColor; + final TextStyle? textStyle; + final int maxValue; + final Duration animationDuration; + + const CustomBadge2({ + super.key, + required this.child, + required this.value, + this.top = -6, + this.right = -6, + this.left = double.nan, + this.bottom = double.nan, + this.minSize = 18, + this.backgroundColor = AppColor.red30, + this.textColor = AppColor.white10, + this.textStyle, + this.maxValue = 99, + this.animationDuration = const Duration(milliseconds: 200), + }); + + bool get _isVisible => value > 0; + + @override + Widget build(BuildContext context) { + return Stack( + clipBehavior: Clip.none, + children: [ + child, + if (_isVisible) + Positioned( + top: top.isNaN ? null : top, + right: right.isNaN ? null : right, + left: left.isNaN ? null : left, + bottom: bottom.isNaN ? null : bottom, + child: AnimatedScale( + scale: _isVisible ? 1 : 0, + duration: animationDuration, + curve: Curves.easeOutBack, + child: AnimatedOpacity( + opacity: _isVisible ? 1 : 0, + duration: animationDuration, + child: _Badge( + value: value, + maxValue: maxValue, + minSize: minSize, + backgroundColor: backgroundColor, + textColor: textColor, + textStyle: textStyle ?? + Theme.of(context).textTheme.labelSmall?.copyWith( + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + ], + ); + } +} + +class _Badge extends StatelessWidget { + final int value; + final int maxValue; + final double minSize; + final Color backgroundColor; + final Color textColor; + final TextStyle? textStyle; + + const _Badge({ + required this.value, + required this.maxValue, + required this.minSize, + required this.backgroundColor, + required this.textColor, + this.textStyle, + }); + + bool get _isOverflow => value > maxValue; + + @override + Widget build(BuildContext context) { + final TextStyle baseStyle = (textStyle ?? Theme.of(context).textTheme.labelMedium)!.copyWith( + color: textColor, + fontWeight: FontWeight.w700, + ); + + final double badgeSize = minSize; + + return IgnorePointer( + child: SizedBox( + width: badgeSize, + height: badgeSize, + child: DecoratedBox( + decoration: const BoxDecoration( + shape: BoxShape.circle, + ), + child: DecoratedBox( + decoration: BoxDecoration( + color: backgroundColor, + shape: BoxShape.circle, + ), + child: Center( + child: _isOverflow + ? FittedBox( + child: Text( + '$maxValue+', + style: baseStyle.copyWith( + fontSize: badgeSize * 0.45, + ), + ), + ) + : CounterAnimatedText( + value: value, + style: baseStyle.copyWith( + fontSize: badgeSize * 0.6, + ), + ), + ), + ), + ), + ), + ); + } +} From 077095502e7d5f92bb09d2a06f9b984638a7ea9b Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Mon, 29 Dec 2025 08:56:20 +0300 Subject: [PATCH 20/35] chat count implemented --- .../notification/firebase_notification_manger.dart | 4 +++- .../cm_module/views/service_request_detail_main_view.dart | 2 +- lib/modules/cx_module/chat/helper/chat_file_viewer.dart | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/controllers/notification/firebase_notification_manger.dart b/lib/controllers/notification/firebase_notification_manger.dart index cc32e159..7b6dd5cd 100644 --- a/lib/controllers/notification/firebase_notification_manger.dart +++ b/lib/controllers/notification/firebase_notification_manger.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:firebase_messaging/firebase_messaging.dart'; @@ -84,6 +85,7 @@ class FirebaseNotificationManger { } static void handleMessage(context, Map messageData) { + log('message data is ${messageData}'); if (messageData["requestType"] != null && messageData["requestNumber"] != null) { Widget? serviceClass; @@ -116,7 +118,7 @@ class FirebaseNotificationManger { moduleId: moduleId, // requestDetails: RequestsDetails(nameOfType: requestData?.nameOfType, status: requestData?.statusName, priority: requestData?.priorityName, date: requestData?.transactionDate, ), ); - ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); + // ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); break; default: diff --git a/lib/modules/cm_module/views/service_request_detail_main_view.dart b/lib/modules/cm_module/views/service_request_detail_main_view.dart index 3e2fab9c..332a7b49 100644 --- a/lib/modules/cm_module/views/service_request_detail_main_view.dart +++ b/lib/modules/cm_module/views/service_request_detail_main_view.dart @@ -41,6 +41,7 @@ class _ServiceRequestDetailMainState extends State { @override void initState() { super.initState(); + _requestProvider = Provider.of(context, listen: false); WidgetsBinding.instance.addPostFrameCallback((_) { Provider.of(context, listen: false).reset(); getInitialData(); @@ -49,7 +50,6 @@ class _ServiceRequestDetailMainState extends State { Future getInitialData() async { bool isNurse = (Provider.of(context, listen: false).user?.type) == UsersTypes.normal_user; - _requestProvider = Provider.of(context, listen: false); await _requestProvider.getWorkOrderById(id: widget.requestId); if (isNurse && (_requestProvider.currentWorkOrder?.data?.nextStep?.workOrderNextStepEnum == WorkOrderNextStepEnum.waitingForRequesterToConfirm)) { ServiceRequestBottomSheet.nurseVerifyArrivalBottomSheet(context: context); diff --git a/lib/modules/cx_module/chat/helper/chat_file_viewer.dart b/lib/modules/cx_module/chat/helper/chat_file_viewer.dart index 04fba4a1..6d9c99d9 100644 --- a/lib/modules/cx_module/chat/helper/chat_file_viewer.dart +++ b/lib/modules/cx_module/chat/helper/chat_file_viewer.dart @@ -24,6 +24,7 @@ class ChatFileViewer extends StatelessWidget { @override Widget build(BuildContext context) { + return FutureBuilder( future: checkFileInLocalStorage(), builder: (BuildContext context, AsyncSnapshot snapshot) { From 66b77c51a61567cd021dc979e6d82ce0c9545148 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Tue, 30 Dec 2025 09:22:51 +0300 Subject: [PATCH 21/35] loan site bug fixes --- .../loan_module/models/loan_form_model.dart | 8 +++++--- .../pages/create_loan_request_page.dart | 15 ++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/modules/loan_module/models/loan_form_model.dart b/lib/modules/loan_module/models/loan_form_model.dart index b31df2e6..a60b93ef 100644 --- a/lib/modules/loan_module/models/loan_form_model.dart +++ b/lib/modules/loan_module/models/loan_form_model.dart @@ -1,7 +1,5 @@ import 'package:test_sa/models/lookup.dart'; -import 'package:test_sa/models/new_models/building.dart'; -import 'package:test_sa/models/new_models/department.dart'; -import 'package:test_sa/models/new_models/floor.dart'; +import 'package:test_sa/models/new_models/mapped_sites.dart'; import 'package:test_sa/models/new_models/site.dart'; import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; @@ -21,6 +19,8 @@ class LoanFormModel { Site? site; MedicalDepartmentModel? department; List? loanAttachment; + MappedSite? mappedSite; + MappedDepartment? mappedDepartment; LoanFormModel({ this.docName, @@ -38,6 +38,8 @@ class LoanFormModel { this.loanAttachment, this.site, this.department, + this.mappedSite, + this.mappedDepartment, }); //{ diff --git a/lib/modules/loan_module/pages/create_loan_request_page.dart b/lib/modules/loan_module/pages/create_loan_request_page.dart index fe76e39b..c257622f 100644 --- a/lib/modules/loan_module/pages/create_loan_request_page.dart +++ b/lib/modules/loan_module/pages/create_loan_request_page.dart @@ -1,22 +1,17 @@ import 'dart:convert'; import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:test_sa/controllers/providers/api/device_transfer_provider.dart'; import 'package:test_sa/controllers/validator/validator.dart'; -import 'package:test_sa/dashboard_latest/dashboard_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/helper/utils.dart'; -import 'package:test_sa/models/device/asset.dart'; -import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/generic_attachment_model.dart'; import 'package:test_sa/models/lookup.dart'; -import 'package:test_sa/models/new_models/site.dart'; +import 'package:test_sa/models/new_models/mapped_sites.dart'; import 'package:test_sa/models/new_models/task_request/task_type_model.dart'; import 'package:test_sa/models/new_models/work_order_detail_model.dart'; import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; @@ -25,13 +20,11 @@ import 'package:test_sa/modules/loan_module/models/loan_form_model.dart'; import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; import 'package:test_sa/modules/loan_module/provider/loan_period_provider.dart'; import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; -import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; import 'package:test_sa/modules/loan_module/provider/medical_department_provider.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/department_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/views/widgets/images/multi_image_picker.dart'; @@ -275,10 +268,10 @@ class _CreateLoanRequestPageState extends State with Tick return [ 'Item Details'.addTranslation.bodyText(context).custom(color: AppColor.black10), 8.height, - SingleItemDropDownMenu( + SingleItemDropDownMenu( context: context, title: context.translation.site, - initialValue: _loanFormModel.site, + initialValue: _loanFormModel.mappedSite, showShadow: false, validator: (value) { if (value == null) return "Please select a site"; @@ -287,7 +280,7 @@ class _CreateLoanRequestPageState extends State with Tick backgroundColor: AppColor.fieldBgColor(context), showAsBottomSheet: true, onSelect: (value) { - _loanFormModel.site = value; + _loanFormModel.mappedSite = value; setState(() {}); }, ), From e303b4e099485e6dc60e553f87c9c3e7d7812642 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Tue, 30 Dec 2025 13:56:00 +0300 Subject: [PATCH 22/35] chat bug fixes --- lib/controllers/api_routes/urls.dart | 1 + .../cx_module/chat/chat_api_client.dart | 44 +++++++++++++++++++ lib/modules/cx_module/chat/chat_page.dart | 6 ++- lib/modules/cx_module/chat/chat_provider.dart | 31 ++++++++++++- lib/modules/cx_module/chat/chat_widget.dart | 6 ++- .../chat/helper/chat_file_picker.dart | 1 + .../chat/model/chat_participant_model.dart | 6 +-- 7 files changed, 88 insertions(+), 7 deletions(-) diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index bd3e73e6..2897df0d 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -17,6 +17,7 @@ class URLs { static const String chatHubUrlApi = "$chatHubUrl/api"; // new V2 apis static const String chatHubUrlChat = "$chatHubUrl/hubs/chat"; // new V2 apis + static const String resetMessageCount = "$chatHubUrlApi/userChatHistory/AckMessages"; // new V2 apis static const String chatApiKey = "f53a98286f82798d588f67a7f0db19f7aebc839e"; // new V2 apis static String _host = host1; diff --git a/lib/modules/cx_module/chat/chat_api_client.dart b/lib/modules/cx_module/chat/chat_api_client.dart index 29d9239a..77770e83 100644 --- a/lib/modules/cx_module/chat/chat_api_client.dart +++ b/lib/modules/cx_module/chat/chat_api_client.dart @@ -95,6 +95,50 @@ class ChatApiClient { } } + Future resetCountApi(int moduleCode, int referenceNumber, String? employeeNumber) async { + final Map payload = { + 'moduleCode': moduleCode, + 'ReferenceNumber': referenceNumber, + }; + + // final headers = chatAckHeaders( + // apiKey: URLs.chatApiKey, + // employeeNumber: employeeNumber.toString(), + // origin: 'http://localhost:4400', + // referer: 'http://localhost:4400/', + // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', + // ); + + final Response response = await ApiClient().postJsonForResponse( + URLs.resetMessageCount, + payload, + token: chatLoginResponse!.token, + // headers: headers, + ); + // Response response = await ApiClient().postJsonForResponse( + // "${URLs.resetMessageCount}?moduleCode=$moduleCode&ReferenceNumber=$referenceNumber", {"moduleCode": moduleCode.toString(), "ReferenceNumber": referenceNumber.toString()}, + // token: chatLoginResponse!.token); + + + return response.statusCode == 200; + } + + Map chatAckHeaders({ + required String apiKey, + required String employeeNumber, + required String origin, + required String referer, + required String userAgent, + String acceptLanguage = 'en-US,en;q=0.9', + }) { + return { + 'Accept': 'application/json, text/plain, */*', + 'Content-Type': 'application/json', + // 'X-API-Key': apiKey, + // 'X-Employee-Number': employeeNumber, + }; + } + Future> viewAllDocuments(int moduleId, int referenceId) async { Response response = await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/attachments/conversation?referenceId=$referenceId&moduleCode=$moduleId", token: chatLoginResponse!.token); diff --git a/lib/modules/cx_module/chat/chat_page.dart b/lib/modules/cx_module/chat/chat_page.dart index f84b4e23..d4b44721 100644 --- a/lib/modules/cx_module/chat/chat_page.dart +++ b/lib/modules/cx_module/chat/chat_page.dart @@ -75,7 +75,7 @@ class _ChatPageState extends State { }); } - void loadChatHistory() { + void loadChatHistory() async { // // String assigneeEmployeeNumber = Provider.of(context, listen: false).currentWorkOrder?.data?.assignedEmployee?.employeeId ?? ""; // // String myEmployeeId = context.userProvider.user!.username!; // // @@ -99,7 +99,9 @@ class _ChatPageState extends State { ? Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderCreatedBy!.employeeId! : Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!)) : ""); - Provider.of(context, listen: false).connectToHub(widget.moduleId, widget.requestId, myEmployeeId, receiver, widget.readOnly, isMounted: mounted); + ChatProvider chatProvider = Provider.of(context, listen: false); + chatProvider.connectToHub(widget.moduleId, widget.requestId, myEmployeeId, receiver, widget.readOnly, isMounted: mounted); + } @override diff --git a/lib/modules/cx_module/chat/chat_provider.dart b/lib/modules/cx_module/chat/chat_provider.dart index 136dd28b..84282a64 100644 --- a/lib/modules/cx_module/chat/chat_provider.dart +++ b/lib/modules/cx_module/chat/chat_provider.dart @@ -40,6 +40,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:signalr_netcore/hub_connection.dart'; import 'package:signalr_netcore/signalr_client.dart'; +import 'package:test_sa/controllers/api_routes/api_manager.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/modules/cx_module/chat/model/chat_login_response_model.dart'; @@ -370,9 +371,37 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { // } } } - // notifyListeners(); } + Future resetCount({ + required int moduleId, + required int referenceNo, + String ?userId , + }) async { + try { + return await ChatApiClient().resetCountApi(moduleId, referenceNo,userId); + } catch (e, stack) { + debugPrint('resetCount error: $e'); + rethrow; + } + } + + // Future resetCount({ required int moduleId, + // required int referenceNo}) async { + // Response response; + // try { + // response = await ApiManager.instance.post("${URLs.resetMessageCount}?moduleCode=$moduleId&ReferenceNumber=$referenceNo", body: {}); + // notifyListeners(); + // if (response.statusCode == 200) { + // return true; + // } + // return false; + // } catch (error) { + // notifyListeners(); + // return false; + // } + // } + void updateUserChatHistoryStatusAsync(List data) { try { chatHubConnection!.invoke("UpdateUserChatHistoryStatusAsync", args: [data]); diff --git a/lib/modules/cx_module/chat/chat_widget.dart b/lib/modules/cx_module/chat/chat_widget.dart index f84c7032..788f470b 100644 --- a/lib/modules/cx_module/chat/chat_widget.dart +++ b/lib/modules/cx_module/chat/chat_widget.dart @@ -86,7 +86,11 @@ class _ChatWidgetState extends State { minSize: 18, child: IconButton( icon: const Icon(Icons.chat_bubble), - onPressed: () { + onPressed: () async { + bool readStatus = await chatProvider.resetCount(moduleId: widget.moduleId, referenceNo: widget.requestId, userId: context.userProvider.user?.id); + if (readStatus) { + chatProvider.chatParticipantModel?.unreadCount = 0; + } Navigator.push( context, CupertinoPageRoute( diff --git a/lib/modules/cx_module/chat/helper/chat_file_picker.dart b/lib/modules/cx_module/chat/helper/chat_file_picker.dart index 1070e3db..f080cd20 100644 --- a/lib/modules/cx_module/chat/helper/chat_file_picker.dart +++ b/lib/modules/cx_module/chat/helper/chat_file_picker.dart @@ -46,6 +46,7 @@ class ChatFilePicker extends StatelessWidget { fromMediaPicker(BuildContext context, ImageSource imageSource) async { XFile? pickedFile = await ImagePicker().pickImage(source: imageSource, imageQuality: 70, maxWidth: 800, maxHeight: 800); if (pickedFile != null) { + //Has UI issues here top buttons not clickable smoothly CroppedFile? croppedFile = await ImageCropper().cropImage( sourcePath: pickedFile.path, // aspectRatio: CropAspectRatio(ratioX: 1, ratioY: 1), diff --git a/lib/modules/cx_module/chat/model/chat_participant_model.dart b/lib/modules/cx_module/chat/model/chat_participant_model.dart index 671bc4c4..80c6bfc4 100644 --- a/lib/modules/cx_module/chat/model/chat_participant_model.dart +++ b/lib/modules/cx_module/chat/model/chat_participant_model.dart @@ -5,7 +5,7 @@ class ChatParticipantModel { String? title; String? conversationType; List? participants; - // String? lastMessage; + dynamic lastMessage; String? createdAt; int? unreadCount; @@ -21,7 +21,7 @@ class ChatParticipantModel { participants!.add(new Participants.fromJson(v)); }); } - // lastMessage = json['lastMessage']; + lastMessage = json['lastMessage']; createdAt = json['createdAt']; unreadCount = json['unreadCount']; } @@ -34,7 +34,7 @@ class ChatParticipantModel { if (participants != null) { data['participants'] = participants!.map((v) => v.toJson()).toList(); } - // data['lastMessage'] = lastMessage; + data['lastMessage'] = lastMessage; data['createdAt'] = createdAt; data['unreadCount'] = unreadCount; return data; From d57348dd92fb3c32392f4593f542736d0c96176c Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Tue, 30 Dec 2025 14:58:21 +0300 Subject: [PATCH 23/35] bug fixes for site and department previously it was showing all sites not related to that specif user --- lib/modules/loan_module/models/loan_form_model.dart | 9 ++++++--- .../loan_module/pages/create_loan_request_page.dart | 9 +++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/modules/loan_module/models/loan_form_model.dart b/lib/modules/loan_module/models/loan_form_model.dart index a60b93ef..79d459ca 100644 --- a/lib/modules/loan_module/models/loan_form_model.dart +++ b/lib/modules/loan_module/models/loan_form_model.dart @@ -1,6 +1,7 @@ import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/new_models/mapped_sites.dart'; import 'package:test_sa/models/new_models/site.dart'; +import 'package:test_sa/models/new_models/traf_department.dart'; import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; class LoanFormModel { @@ -20,7 +21,7 @@ class LoanFormModel { MedicalDepartmentModel? department; List? loanAttachment; MappedSite? mappedSite; - MappedDepartment? mappedDepartment; + TrafDepartment? mappedDepartment; LoanFormModel({ this.docName, @@ -93,8 +94,10 @@ class LoanFormModel { "vendorNumber": vendorNumber, "vendorContact": vendorNumber, "vendorEmail": vendorEmail, - 'siteId': site?.id, - 'departmentId': department?.id, + // 'siteId': site?.id, + 'siteId': mappedSite?.id, + // 'departmentId': department?.id, + 'departmentId': mappedDepartment?.id, "loanAttachments": loanAttachment != null ? loanAttachment!.map((v) => v.toJson()).toList() : [], }; } diff --git a/lib/modules/loan_module/pages/create_loan_request_page.dart b/lib/modules/loan_module/pages/create_loan_request_page.dart index c257622f..b5107f85 100644 --- a/lib/modules/loan_module/pages/create_loan_request_page.dart +++ b/lib/modules/loan_module/pages/create_loan_request_page.dart @@ -13,11 +13,11 @@ import 'package:test_sa/models/generic_attachment_model.dart'; import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/new_models/mapped_sites.dart'; import 'package:test_sa/models/new_models/task_request/task_type_model.dart'; +import 'package:test_sa/models/new_models/traf_department.dart'; import 'package:test_sa/models/new_models/work_order_detail_model.dart'; import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/modules/loan_module/models/loan_form_model.dart'; -import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; import 'package:test_sa/modules/loan_module/provider/loan_period_provider.dart'; import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; import 'package:test_sa/modules/loan_module/provider/medical_department_provider.dart'; @@ -27,6 +27,7 @@ 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/gas_request_providers/site_provider.dart'; import 'package:test_sa/providers/loading_list_notifier.dart'; +import 'package:test_sa/providers/lookups/department_lookup_provider.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; import '../../../../../../new_views/common_widgets/default_app_bar.dart'; @@ -285,7 +286,7 @@ class _CreateLoanRequestPageState extends State with Tick }, ), 8.height, - SingleItemDropDownMenu( + SingleItemDropDownMenu( context: context, title: context.translation.department, showShadow: false, @@ -294,11 +295,11 @@ class _CreateLoanRequestPageState extends State with Tick return null; }, showAsBottomSheet: true, - initialValue: _loanFormModel.department, + initialValue: _loanFormModel.mappedDepartment, requestById: context.userProvider.user?.clientId, backgroundColor: AppColor.fieldBgColor(context), onSelect: (value) { - _loanFormModel.department = value; + _loanFormModel.mappedDepartment = value; setState(() {}); }, ), From af6ae868d7fe816b8732faab3bc7569e3d0970b9 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Tue, 30 Dec 2025 16:23:44 +0300 Subject: [PATCH 24/35] improvements --- .../loan_module/pages/create_loan_request_page.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/modules/loan_module/pages/create_loan_request_page.dart b/lib/modules/loan_module/pages/create_loan_request_page.dart index b5107f85..32616761 100644 --- a/lib/modules/loan_module/pages/create_loan_request_page.dart +++ b/lib/modules/loan_module/pages/create_loan_request_page.dart @@ -281,8 +281,13 @@ class _CreateLoanRequestPageState extends State with Tick backgroundColor: AppColor.fieldBgColor(context), showAsBottomSheet: true, onSelect: (value) { - _loanFormModel.mappedSite = value; - setState(() {}); + if(value!=null){ + _loanFormModel.mappedSite = value; + _loanFormModel.mappedDepartment = null; + Provider.of(context,listen:false).getData(id:_loanFormModel.mappedSite?.id); + setState(() {}); + } + }, ), 8.height, From e598f3b5ad71d5c840129a46f260ff74d62baced Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Wed, 31 Dec 2025 09:39:36 +0300 Subject: [PATCH 25/35] chat bug fixes --- .../cx_module/chat/chat_api_client.dart | 35 ++----------------- lib/modules/cx_module/chat/chat_page.dart | 14 +++++--- lib/modules/cx_module/chat/chat_provider.dart | 18 ++-------- lib/modules/cx_module/chat/chat_widget.dart | 14 ++++---- 4 files changed, 21 insertions(+), 60 deletions(-) diff --git a/lib/modules/cx_module/chat/chat_api_client.dart b/lib/modules/cx_module/chat/chat_api_client.dart index 77770e83..639849a3 100644 --- a/lib/modules/cx_module/chat/chat_api_client.dart +++ b/lib/modules/cx_module/chat/chat_api_client.dart @@ -35,6 +35,7 @@ import 'model/user_chat_history_model.dart'; // import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as user; // import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav; +//Need to refector this remove unused code. class ChatApiClient { static final ChatApiClient _instance = ChatApiClient._internal(); @@ -97,47 +98,17 @@ class ChatApiClient { Future resetCountApi(int moduleCode, int referenceNumber, String? employeeNumber) async { final Map payload = { - 'moduleCode': moduleCode, - 'ReferenceNumber': referenceNumber, + 'moduleCode': "$moduleCode", + 'ReferenceNumber': "$referenceNumber", }; - - // final headers = chatAckHeaders( - // apiKey: URLs.chatApiKey, - // employeeNumber: employeeNumber.toString(), - // origin: 'http://localhost:4400', - // referer: 'http://localhost:4400/', - // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', - // ); - final Response response = await ApiClient().postJsonForResponse( URLs.resetMessageCount, payload, token: chatLoginResponse!.token, - // headers: headers, ); - // Response response = await ApiClient().postJsonForResponse( - // "${URLs.resetMessageCount}?moduleCode=$moduleCode&ReferenceNumber=$referenceNumber", {"moduleCode": moduleCode.toString(), "ReferenceNumber": referenceNumber.toString()}, - // token: chatLoginResponse!.token); - - return response.statusCode == 200; } - Map chatAckHeaders({ - required String apiKey, - required String employeeNumber, - required String origin, - required String referer, - required String userAgent, - String acceptLanguage = 'en-US,en;q=0.9', - }) { - return { - 'Accept': 'application/json, text/plain, */*', - 'Content-Type': 'application/json', - // 'X-API-Key': apiKey, - // 'X-Employee-Number': employeeNumber, - }; - } Future> viewAllDocuments(int moduleId, int referenceId) async { Response response = await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/attachments/conversation?referenceId=$referenceId&moduleCode=$moduleId", token: chatLoginResponse!.token); diff --git a/lib/modules/cx_module/chat/chat_page.dart b/lib/modules/cx_module/chat/chat_page.dart index d4b44721..060bd52b 100644 --- a/lib/modules/cx_module/chat/chat_page.dart +++ b/lib/modules/cx_module/chat/chat_page.dart @@ -28,7 +28,7 @@ import 'helper/chat_file_picker.dart'; import 'helper/chat_file_viewer.dart'; import 'model/get_single_user_chat_list_model.dart'; import 'model/user_chat_history_model.dart'; - +//Need to refactor this ... enum ChatState { idle, voiceRecordingStarted, voiceRecordingCompleted } class ChatPage extends StatefulWidget { @@ -66,7 +66,10 @@ class _ChatPageState extends State { @override void initState() { super.initState(); - loadChatHistory(); + WidgetsBinding.instance.addPostFrameCallback((_) { + loadChatHistory(); + }); + playerController.addListener(() async { // if (playerController.playerState == PlayerState.playing && playerController.maxDuration == await playerController.getDuration()) { // await playerController.stopPlayer(); @@ -99,9 +102,12 @@ class _ChatPageState extends State { ? Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderCreatedBy!.employeeId! : Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!)) : ""); - ChatProvider chatProvider = Provider.of(context, listen: false); + final chatProvider = context.read(); chatProvider.connectToHub(widget.moduleId, widget.requestId, myEmployeeId, receiver, widget.readOnly, isMounted: mounted); - + bool readStatus = await chatProvider.resetCount(moduleId: widget.moduleId, referenceNo: widget.requestId, userId: context.userProvider.user?.id); + if (readStatus) { + chatProvider.chatParticipantModel?.unreadCount = 0; + } } @override diff --git a/lib/modules/cx_module/chat/chat_provider.dart b/lib/modules/cx_module/chat/chat_provider.dart index 84282a64..46ba0d3c 100644 --- a/lib/modules/cx_module/chat/chat_provider.dart +++ b/lib/modules/cx_module/chat/chat_provider.dart @@ -55,6 +55,8 @@ import 'model/get_single_user_chat_list_model.dart'; import 'model/user_chat_history_model.dart'; // import 'get_single_user_chat_list_model.dart'; +//Need to refactor this remove unused code. + HubConnection? chatHubConnection; class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { @@ -386,22 +388,6 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { } } - // Future resetCount({ required int moduleId, - // required int referenceNo}) async { - // Response response; - // try { - // response = await ApiManager.instance.post("${URLs.resetMessageCount}?moduleCode=$moduleId&ReferenceNumber=$referenceNo", body: {}); - // notifyListeners(); - // if (response.statusCode == 200) { - // return true; - // } - // return false; - // } catch (error) { - // notifyListeners(); - // return false; - // } - // } - void updateUserChatHistoryStatusAsync(List data) { try { chatHubConnection!.invoke("UpdateUserChatHistoryStatusAsync", args: [data]); diff --git a/lib/modules/cx_module/chat/chat_widget.dart b/lib/modules/cx_module/chat/chat_widget.dart index 788f470b..43c5d5fc 100644 --- a/lib/modules/cx_module/chat/chat_widget.dart +++ b/lib/modules/cx_module/chat/chat_widget.dart @@ -1,4 +1,4 @@ -import 'dart:developer'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -43,8 +43,10 @@ class _ChatWidgetState extends State { @override void initState() { super.initState(); - Provider.of(context, listen: false).reset(); - getChatToken(); + WidgetsBinding.instance.addPostFrameCallback((_) { + Provider.of(context, listen: false).reset(); + getChatToken(); + }); } void getChatToken() { @@ -86,11 +88,7 @@ class _ChatWidgetState extends State { minSize: 18, child: IconButton( icon: const Icon(Icons.chat_bubble), - onPressed: () async { - bool readStatus = await chatProvider.resetCount(moduleId: widget.moduleId, referenceNo: widget.requestId, userId: context.userProvider.user?.id); - if (readStatus) { - chatProvider.chatParticipantModel?.unreadCount = 0; - } + onPressed: () { Navigator.push( context, CupertinoPageRoute( From 45d736a64ee098506510bfc487dabd1ff671d925 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 1 Jan 2026 09:24:39 +0300 Subject: [PATCH 26/35] app release v1.5.0+32 to stores --- lib/controllers/api_routes/urls.dart | 10 +-- .../service_request_detail_main_view.dart | 2 +- lib/modules/cx_module/chat/chat_page.dart | 10 ++- lib/modules/cx_module/chat/chat_provider.dart | 68 ++++++++++++++++++- .../tasks_wo/task_request_detail_view.dart | 2 +- .../create_request-type_bottomsheet.dart | 12 ++-- .../my_request/all_requests_filter_page.dart | 24 +++---- .../my_request/my_requests_page.dart | 15 ++-- .../device_transfer_details.dart | 4 +- .../user/gas_refill/gas_refill_details.dart | 2 +- .../widgets/equipment/asset_detail_page.dart | 2 +- 11 files changed, 112 insertions(+), 39 deletions(-) diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 2897df0d..c1b7f5ef 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -1,16 +1,16 @@ class URLs { URLs._(); - static const String appReleaseBuildNumber = "31"; + static const String appReleaseBuildNumber = "32"; - // static const host1 = "https://atomsm.hmg.com"; // production url + static const host1 = "https://atomsm.hmg.com"; // production url // static const host1 = "https://atomsmdev.hmg.com"; // local DEV url - static const host1 = "https://atomsmuat.hmg.com"; // local UAT url + // static const host1 = "https://atomsmuat.hmg.com"; // local UAT url // static final String _baseUrl = "$_host/mobile"; // host local UAT - static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis + // static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis // static final String _baseUrl = "$_host/mobile"; // host local UAT and for internal audit dev - // static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM + static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM // static final String _baseUrl = "$_host/v5/mobile"; // v5 for data segregation static const String chatHubUrl = "https://apiderichat.hmg.com/chathub"; diff --git a/lib/modules/cm_module/views/service_request_detail_main_view.dart b/lib/modules/cm_module/views/service_request_detail_main_view.dart index 332a7b49..f59b49e1 100644 --- a/lib/modules/cm_module/views/service_request_detail_main_view.dart +++ b/lib/modules/cm_module/views/service_request_detail_main_view.dart @@ -102,7 +102,7 @@ class _ServiceRequestDetailMainState extends State { (provider.currentWorkOrder?.data?.workOrderContactPerson?.isNotEmpty ?? false)) { return ChatWidget( moduleId: widget.moduleId, - isShow: (context.userProvider.isEngineer || context.userProvider.isNurse) && provider.currentWorkOrder?.data?.assignedEmployee != null, + isShow: context.settingProvider.isUserFlowMedical && (context.userProvider.isEngineer || context.userProvider.isNurse) && provider.currentWorkOrder?.data?.assignedEmployee != null, isReadOnly: _requestProvider.isReadOnlyRequest, requestId: widget.requestId.toInt(), assigneeEmployeeNumber: provider.currentWorkOrder?.data?.assignedEmployee!.employeeId!, diff --git a/lib/modules/cx_module/chat/chat_page.dart b/lib/modules/cx_module/chat/chat_page.dart index 060bd52b..0cbc72be 100644 --- a/lib/modules/cx_module/chat/chat_page.dart +++ b/lib/modules/cx_module/chat/chat_page.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:audio_waveforms/audio_waveforms.dart'; @@ -28,6 +29,7 @@ import 'helper/chat_file_picker.dart'; import 'helper/chat_file_viewer.dart'; import 'model/get_single_user_chat_list_model.dart'; import 'model/user_chat_history_model.dart'; + //Need to refactor this ... enum ChatState { idle, voiceRecordingStarted, voiceRecordingCompleted } @@ -103,7 +105,9 @@ class _ChatPageState extends State { : Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!)) : ""); final chatProvider = context.read(); + // final chatProvider = Provider.of { } return Column(mainAxisSize: MainAxisSize.min, children: [ if (showDateHeader) dateCard(currentMessage.createdDate?.toString().chatMsgDateWithYear ?? ""), - isSender ? senderMsgCard(showSenderName, chatProvider.chatResponseList[index]) : recipientMsgCard(showSenderName, chatProvider.chatResponseList[index]) + isSender + ? senderMsgCard(showSenderName, chatProvider.chatResponseList[index], index: index) + : recipientMsgCard(showSenderName, chatProvider.chatResponseList[index]) ]); }, itemCount: chatProvider.chatResponseList.length)) @@ -615,7 +621,7 @@ class _ChatPageState extends State { .center; } - Widget senderMsgCard(bool showHeader, SingleUserChatModel? chatResponse, {bool loading = false, String msg = ""}) { + Widget senderMsgCard(bool showHeader, SingleUserChatModel? chatResponse, {bool loading = false, String msg = "", int index = 0}) { Widget senderHeader = Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/modules/cx_module/chat/chat_provider.dart b/lib/modules/cx_module/chat/chat_provider.dart index 46ba0d3c..8d56bcf2 100644 --- a/lib/modules/cx_module/chat/chat_provider.dart +++ b/lib/modules/cx_module/chat/chat_provider.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'dart:typed_data'; import 'package:audio_waveforms/audio_waveforms.dart'; @@ -251,6 +252,9 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { chatHubConnection!.on("OnSubmitChatAsync", OnSubmitChatAsync); chatHubConnection!.on("OnTypingAsync", OnTypingAsync); chatHubConnection!.on("OnStopTypingAsync", OnStopTypingAsync); + //Need by Chat Backend for seen and un seen. + chatHubConnection!.on("OnSeenChatUserAsync", onSeenUserChatAsync); + chatHubConnection!.on("OnAckSeenAsync", onAckSeenAsync); //group On message @@ -378,10 +382,10 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { Future resetCount({ required int moduleId, required int referenceNo, - String ?userId , + String? userId, }) async { try { - return await ChatApiClient().resetCountApi(moduleId, referenceNo,userId); + return await ChatApiClient().resetCountApi(moduleId, referenceNo, userId); } catch (e, stack) { debugPrint('resetCount error: $e'); rethrow; @@ -468,6 +472,15 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { print(args); } + Future markMessageAsRead(int messageId) async { + final senderId = sender?.userId; + if (senderId == null) return; + chatHubConnection?.invoke( + "SendMessageReadAsync", + args: [messageId, senderId], + ); + } + void onChatSeen(List? args) { dynamic items = args!.toList(); // for (var user in searchedChats!) { @@ -574,6 +587,57 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { notifyListeners(); } + Future onSeenUserChatAsync(List? parameters) async { + try { + if (parameters == null || parameters.isEmpty) { + log('onSeenUserChatAsync: parameters are null or empty'); + return; + } + final parm = parameters.first; + if (parm is! List || parm.isEmpty) { + log('onSeenUserChatAsync: parm is not a valid list'); + return; + } + final firstItem = parm.first; + if (firstItem is! Map) { + log('onSeenUserChatAsync: firstItem is not a Map'); + return; + } + await chatHubConnection!.invoke( + "AckSeenAsync", + args: [ + firstItem['currentUserId'], + [firstItem['userChatHistoryId']], + ], + ); + } catch (e, stackTrace) { + log('onSeenUserChatAsync error: $e'); + log('StackTrace: $stackTrace'); + } + } + + + Future onAckSeenAsync(List? parameters) async { + try { + if (parameters == null || parameters.isEmpty) { + log('onAckSeenAsync: parameters are null or empty'); + return; + } + final parm = parameters.first; + log('parm onAckSeenAsync $parm'); + if (chatResponseList.isEmpty) { + log('onAckSeenAsync: chatResponseList is empty'); + return; + } + log('last list item id ${chatResponseList.first.toJson()}'); + chatResponseList.first.isSeen = true; + notifyListeners(); + } catch (e, stackTrace) { + log('onAckSeenAsync error: $e'); + log('StackTrace: $stackTrace'); + } + } + // Future OnSubmitChatAsync(List? parameters) async { // // List data = jsonDecode(parameters!.first!.toString()); // diff --git a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart index e4777fec..bf3aa96f 100644 --- a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart @@ -80,7 +80,7 @@ class _TaskRequestDetailsViewState extends State { (_taskReqModel.taskJobContactPersons?.isNotEmpty ?? false)) { return ChatWidget( moduleId: widget.moduleId, - isShow: _taskReqModel.taskJobStatus!.value! != 1, + isShow: context.settingProvider.isUserFlowMedical&& _taskReqModel.taskJobStatus!.value! != 1, isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, requestId: widget.taskId.toInt(), assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!, diff --git a/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart b/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart index a8d9155c..77439b73 100644 --- a/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart +++ b/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart @@ -110,12 +110,14 @@ class CreateRequestModel { // list.add(CreateRequestModel(module.module!.name!, "add_icon", CreateTaskView.id)); // } // }); - if (context.userProvider.isQualityUser) { + if (context.userProvider.isQualityUser && context.settingProvider.isUserFlowMedical) { list.add(CreateRequestModel("Equipment Internal Audit Checklist".addTranslation, "add_icon", CreateEquipmentInternalAuditForm.id)); list.add(CreateRequestModel("System Internal Audit Checklist".addTranslation, "add_icon", CreateSystemInternalAuditForm.id)); - } else if (context.userProvider.isAssessor) { - list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id)); - } else if (context.userProvider.isEngineer) { + } + // else if (context.userProvider.isAssessor) { + // list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id)); + // } + else if (context.userProvider.isEngineer) { if (Provider.of(context, listen: false).engineerCanCreateCM) { list.add(CreateRequestModel(context.translation.correctiveMaintenance, "add_icon", CreateNewRequest.id)); } @@ -127,7 +129,7 @@ class CreateRequestModel { list.add(CreateRequestModel(context.translation.transferAsset, "add_icon", CreateDeviceTransferRequest.id)); //TODO uncommit this to enable task. list.add(CreateRequestModel(context.translation.task, "add_icon", CreateTaskView.id)); - list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id)); + // list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id)); } return list; } diff --git a/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart index 155babc9..ea79339f 100644 --- a/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart +++ b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart @@ -78,8 +78,8 @@ class _AllRequestsFilterPageState extends State { if (isEngineer) { types[context.translation.recurrentWo] = 5; - types["Equipment Internal Audit".addTranslation] = 10; - types["System Internal Audit".addTranslation] = 11; + if (context.settingProvider.isUserFlowMedical) types["Equipment Internal Audit".addTranslation] = 10; + if (context.settingProvider.isUserFlowMedical) types["System Internal Audit".addTranslation] = 11; } if (context.settingProvider.isUserFlowMedical && isEngineer) { @@ -92,18 +92,18 @@ class _AllRequestsFilterPageState extends State { types[module.module!.name!] = module.module!.value!; } }); - - if (!isEngineer) { - types['TRAF'] = 9; - } - - if (context.userProvider.isAssessor) { - types = {"TRAF": 9}; - } +//Hide traf + // if (!isEngineer) { + // types['TRAF'] = 9; + // } + + // if (context.userProvider.isAssessor) { + // types = {"TRAF": 9}; + // } if (context.userProvider.isQualityUser) { if (context.settingProvider.isUserFlowMedical) types = {'Recall and Alert': 7}; - types['Equipment Internal Audit'] = 10; - types['System Internal Audit'] = 11; + if (context.settingProvider.isUserFlowMedical) types['Equipment Internal Audit'] = 10; + if (context.settingProvider.isUserFlowMedical) types['System Internal Audit'] = 11; } final statuses = { diff --git a/lib/new_views/pages/land_page/my_request/my_requests_page.dart b/lib/new_views/pages/land_page/my_request/my_requests_page.dart index a39cc687..30ba4c19 100644 --- a/lib/new_views/pages/land_page/my_request/my_requests_page.dart +++ b/lib/new_views/pages/land_page/my_request/my_requests_page.dart @@ -61,10 +61,11 @@ class _MyRequestsPageState extends State { requestsList.add(Request(module.module!.value!, module.module!.name!.addTranslation)); } }); - if (context.userProvider.user!.type == UsersTypes.normal_user) { - requestsList.add(Request(9, 'TRAF')); - } - if (context.userProvider.isEngineer) { + //Hide TRAF. + // if (context.userProvider.user!.type == UsersTypes.normal_user) { + // requestsList.add(Request(9, 'TRAF')); + // } + if (context.userProvider.isEngineer &&context.settingProvider.isUserFlowMedical) { requestsList.add(Request(10, 'Equipment Internal Audit')); requestsList.add(Request(11, 'System Internal Audit')); } @@ -72,15 +73,15 @@ class _MyRequestsPageState extends State { if (context.userProvider.isAssessor) { requestsList = [ Request(null, context.translation.allWorkOrder), - Request(9, 'TRAF'), + // Request(9, 'TRAF'), ]; } if (context.userProvider.isQualityUser) { requestsList = [ Request(null, context.translation.allWorkOrder), if (context.settingProvider.isUserFlowMedical) Request(7, 'Recall and Alert'), - Request(10, 'Equipment Internal Audit'), - Request(11, 'System Internal Audit'), + if (context.settingProvider.isUserFlowMedical) Request(10, 'Equipment Internal Audit'), + if (context.settingProvider.isUserFlowMedical) Request(11, 'System Internal Audit'), ]; } diff --git a/lib/views/pages/device_transfer/device_transfer_details.dart b/lib/views/pages/device_transfer/device_transfer_details.dart index 03b04961..e0ad05bc 100644 --- a/lib/views/pages/device_transfer/device_transfer_details.dart +++ b/lib/views/pages/device_transfer/device_transfer_details.dart @@ -223,7 +223,7 @@ class _DeviceTransferDetailsState extends State { if (context.userProvider.isEngineer || context.userProvider.isNurse) ChatWidget( moduleId: widget.moduleId, - isShow: _model!.senderMachineStatusValue! != 4, + isShow: context.settingProvider.isUserFlowMedical && _model!.senderMachineStatusValue! != 4, isReadOnly: _model!.senderMachineStatusValue! != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: _model!.senderAssignedEmployeeNumber, @@ -284,7 +284,7 @@ class _DeviceTransferDetailsState extends State { if (context.userProvider.isEngineer || context.userProvider.isNurse) ChatWidget( moduleId: widget.moduleId, - isShow: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue!) != 0, + isShow: context.settingProvider.isUserFlowMedical && (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue!) != 0, isReadOnly: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue) != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: isSender ? _deviceTransfer.senderAssignedEmployeeNumber! : _deviceTransfer.receiverAssignedEmployeeNumber!, diff --git a/lib/views/pages/user/gas_refill/gas_refill_details.dart b/lib/views/pages/user/gas_refill/gas_refill_details.dart index 57f81d36..74f4287e 100644 --- a/lib/views/pages/user/gas_refill/gas_refill_details.dart +++ b/lib/views/pages/user/gas_refill/gas_refill_details.dart @@ -68,7 +68,7 @@ class _GasRefillDetailsPageState extends State { return ChatWidget( moduleId: widget.moduleId, - isShow: _gasRefillModel.status!.value! != 0, + isShow: context.settingProvider.isUserFlowMedical && _gasRefillModel.status!.value! != 0, isReadOnly: _gasRefillModel.status!.value! != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: _gasRefillModel.assignedEmployee?.employeeId!, diff --git a/lib/views/widgets/equipment/asset_detail_page.dart b/lib/views/widgets/equipment/asset_detail_page.dart index f34f834c..f0b095fb 100644 --- a/lib/views/widgets/equipment/asset_detail_page.dart +++ b/lib/views/widgets/equipment/asset_detail_page.dart @@ -122,7 +122,7 @@ class _AssetDetailPageState extends State { "${context.translation.building}: ${assetModel.building?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context), "${context.translation.floor}: ${assetModel.floor?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context), "${context.translation.md}: ${assetModel.department?.departmentName?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context), - "${context.translation.room}: ${assetModel.room?.value ?? "-"}".bodyText(context), + "${context.translation.room}: ${assetModel.room?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context), ], ).expanded, ], From 45eaa0eeed76547d609ce4bab4dc2040e3b82bf9 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 5 Jan 2026 16:19:54 +0300 Subject: [PATCH 27/35] app release v1.5.0+32 to stores --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 4efdf850..ec7b7255 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.4.0+31 +version: 1.5.0+32 environment: sdk: ">=3.5.0 <4.0.0" From 3944a549b229149dda8da4cceddf47623f9ad587 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Wed, 14 Jan 2026 09:20:32 +0300 Subject: [PATCH 28/35] requested changes done --- lib/controllers/api_routes/urls.dart | 9 +- .../providers/api/all_requests_provider.dart | 6 +- .../providers/api/user_provider.dart | 1 + lib/dashboard_latest/dashboard_provider.dart | 92 ++++-- lib/extensions/enum_extensions.dart | 6 + lib/main.dart | 2 + lib/models/enums/user_types.dart | 3 +- lib/models/user.dart | 3 + .../helper_function.dart | 72 +++++ .../models/cost_center_model.dart | 5 - .../cost_center_serial_no_form_model.dart | 53 +++ .../delivery_inspection_form_model.dart | 30 +- .../models/inspection_person_model.dart | 15 + .../pages/attachment_view.dart | 133 ++++---- .../cost_center_form_view.dart | 305 +++++++++++++----- .../delivery_inspection_form_view.dart | 274 ++++++++++++---- .../inpection_bottomsheet.dart | 152 +++++++++ .../end_user_cost_center_list_view.dart | 8 +- .../technical_inspection_lines_list_view.dart | 14 +- .../provider/asset_delivery_provider.dart | 38 ++- .../provider/attachment_type_provider.dart | 33 ++ .../my_request/all_requests_filter_page.dart | 6 + .../my_request/my_requests_page.dart | 9 +- 23 files changed, 1017 insertions(+), 252 deletions(-) create mode 100644 lib/modules/asset_delivery_module/helper_function.dart create mode 100644 lib/modules/asset_delivery_module/models/cost_center_serial_no_form_model.dart create mode 100644 lib/modules/asset_delivery_module/models/inspection_person_model.dart create mode 100644 lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart create mode 100644 lib/modules/asset_delivery_module/provider/attachment_type_provider.dart diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index bacf721a..2c8c2a6b 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -68,9 +68,11 @@ class URLs { //service request new flow urls. static get nurseDashboardCountUrl => '$_baseUrl/ServiceRequest/GetDashboardNurseCount'; static get qaAuditorCount => '$_baseUrl/ServiceRequest/GetDashboardQAAuditorCount'; + static get commissioningEngineerCount => '$_baseUrl/ServiceRequest/GetDashboardCommissioningEngineerCount'; static get nurseDashboardDetailsUrl => '$_baseUrl/ServiceRequest/GetDashboardNurseDetails'; static get qaAuditorDetails => '$_baseUrl/ServiceRequest/GetDashboardQAAuditorDetails'; + static get commissioningEngineerDetails => '$_baseUrl/ServiceRequest/GetDashboardCommissioningEngineerDetails'; static get nurseRejectUrl => '$_baseUrl/ServiceRequest/NurseReject'; @@ -413,8 +415,10 @@ class URLs { static get getLineFromExternalPo => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/GetLineFromExternalPo'; static get getCostCenterListToLineDeliveryInspection => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/GetCostCenterToLineDeliveryInspection'; + static get getCostCenterAndSerialNoToLineDeliveryInspection => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/GetCostCenterAndSerialNoToLineDeliveryInspection'; static get getCostCenterToPo => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/GetCostCenterToPo'; static get saveCostCenterToLineDeliveryInspection => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/SaveCostCenterToLineDeliveryInspection'; + static get saveCostCenterAndSerialNoToLineDeliveryInspection => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/SaveCostCenterAndSerialNoToLineDeliveryInspection'; static get getDeliveryHistories => '$_baseUrl/AssetDeliveryExternalDelivery/GetDeliveryHistories'; //Technical Inscption... static get getLineFromTechnicalInspection => '$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetLineFromDeliveryInspection'; @@ -425,7 +429,7 @@ class URLs { static get getAssetFromInternalTechnicalInspection => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetAssetFromInternal"; static get getAssetDetailsByAssetId => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetAssetByIdFromInternal"; static get saveTechnicalInspectionAsset => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/SaveTechnicalInspectionItem"; - static get getAttachments => "$_baseUrl/AssetDeliveryExternalDeliveryAttachment/GetAttachments"; + static get getAttachments => "$_baseUrl/AssetDeliveryExternalDeliveryAttachment/GetAttachmentsBasedOnType"; static get saveAttachments => "$_baseUrl/AssetDeliveryExternalDeliveryAttachment/Save"; //End user Acceptance... static get getEndUserAcceptanceCostCenter => "$_baseUrl/AssetDeliveryEndUserAcceptance/GetEndUserAcceptanceCostCenter"; @@ -433,5 +437,6 @@ class URLs { static get saveEndUserFormData => "$_baseUrl/AssetDeliveryEndUserAcceptance/SaveMaster"; static get saveEndUserAssetData => "$_baseUrl/AssetDeliveryEndUserAcceptance/SaveDetail"; static get endUserStatusLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=534"; - static get endUserRejectionReasonLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=5007"; + static get endUserRejectionReasonLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=415"; + static get attachmentTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=5009"; } diff --git a/lib/controllers/providers/api/all_requests_provider.dart b/lib/controllers/providers/api/all_requests_provider.dart index 5036ea9c..86579328 100644 --- a/lib/controllers/providers/api/all_requests_provider.dart +++ b/lib/controllers/providers/api/all_requests_provider.dart @@ -166,7 +166,11 @@ class AllRequestsProvider extends ChangeNotifier { if (context.userProvider.isNurse) { list.add(9); // } - list.add(12); // loan module + list.add(12); + if (context.userProvider.isCommissioningEngineer) { + list = [13]; + } + // loan module return list; } diff --git a/lib/controllers/providers/api/user_provider.dart b/lib/controllers/providers/api/user_provider.dart index ad64c8b8..d82a6b2d 100644 --- a/lib/controllers/providers/api/user_provider.dart +++ b/lib/controllers/providers/api/user_provider.dart @@ -47,6 +47,7 @@ class UserProvider extends ChangeNotifier { bool get isAssessor => user!.type == UsersTypes.assessor || user!.type == UsersTypes.assessorTl; bool get isQualityUser => user!.type == UsersTypes.qualityUser; + bool get isCommissioningEngineer => user!.type == UsersTypes.commissioningEngineer; VerifyOtpModel _verifyOtpModel = VerifyOtpModel(); SwipeTransaction _swipeTransactionModel = SwipeTransaction(); diff --git a/lib/dashboard_latest/dashboard_provider.dart b/lib/dashboard_latest/dashboard_provider.dart index f1a92f54..cc39807f 100644 --- a/lib/dashboard_latest/dashboard_provider.dart +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -106,13 +106,22 @@ class DashBoardProvider extends ChangeNotifier { isAllCountLoading = true; notifyListeners(); String url = ''; - if (usersType == UsersTypes.qualityUser) { - url = URLs.qaAuditorCount; - } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { - url = URLs.engineerDashboardCountUrl; - } else { - url = URLs.nurseDashboardCountUrl; - } + + // if (usersType == UsersTypes.qualityUser) { + // url = URLs.qaAuditorCount; + // } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { + // url = URLs.engineerDashboardCountUrl; + // } else { + // url = URLs.nurseDashboardCountUrl; + // } + final urlMap = { + UsersTypes.qualityUser: URLs.qaAuditorCount, + UsersTypes.commissioningEngineer: URLs.commissioningEngineerCount, + UsersTypes.engineer: URLs.engineerDashboardCountUrl, + UsersTypes.assessor: URLs.engineerDashboardCountUrl, + UsersTypes.assessorTl: URLs.engineerDashboardCountUrl, + }; + url = urlMap[usersType] ?? URLs.nurseDashboardCountUrl; Response response; try { response = await ApiManager.instance.post(url, body: {}); @@ -235,6 +244,32 @@ class DashBoardProvider extends ChangeNotifier { } } + String getDashboardUrl({ + required UsersTypes usersType, + int? status, + int? tabId, + }) { + switch (usersType) { + case UsersTypes.qualityUser: + return URLs.qaAuditorDetails; + case UsersTypes.commissioningEngineer: + return URLs.commissioningEngineerDetails; + case UsersTypes.engineer: + case UsersTypes.assessor: + case UsersTypes.assessorTl: + if (status == 0) { + return URLs.engineerDashboardUpcoming; + } else if (status == 1 && tabId == 1) { + return URLs.engineerDashboardNotAssignDetails; + } else { + // status == 1 && tabId == 2 OR any other case + return URLs.engineerDashboardDetailsUrl; + } + default: + return URLs.nurseDashboardDetailsUrl; + } + } + Future getRequestDetail({bool showLoader = true, required UsersTypes usersType, int? status, bool isHighPriority = false, bool isOverdue = false, String? date, int? tabId}) async { if (showLoader) { isDetailLoading = showLoader; @@ -243,25 +278,26 @@ class DashBoardProvider extends ChangeNotifier { Response response; String url = ''; + // if (usersType == UsersTypes.qualityUser) { + // url = URLs.qaAuditorDetails; + // } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { + // //need to check pagination for not assigned task @waseem. + // + // //these checks are to call different apis for dashboard for engineer... + // if (status == 0) { + // url = URLs.engineerDashboardUpcoming; + // } else if (status == 1 && tabId == 1) { + // url = URLs.engineerDashboardNotAssignDetails; + // } else if (status == 1 && tabId == 2) { + // url = URLs.engineerDashboardDetailsUrl; + // } else { + // url = URLs.engineerDashboardDetailsUrl; + // } + // } else { + // url = URLs.nurseDashboardDetailsUrl; + // } + url = getDashboardUrl(usersType: usersType, status: status, tabId: tabId); - if (usersType == UsersTypes.qualityUser) { - url = URLs.qaAuditorDetails; - } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { - //need to check pagination for not assigned task @waseem. - - //these checks are to call different apis for dashboard for engineer... - if (status == 0) { - url = URLs.engineerDashboardUpcoming; - } else if (status == 1 && tabId == 1) { - url = URLs.engineerDashboardNotAssignDetails; - } else if (status == 1 && tabId == 2) { - url = URLs.engineerDashboardDetailsUrl; - } else { - url = URLs.engineerDashboardDetailsUrl; - } - } else { - url = URLs.nurseDashboardDetailsUrl; - } try { Map body = {"pageNumber": pageNum, "pageSize": pageItemNumber}; @@ -383,6 +419,12 @@ class CategoryTabs { tabs.add(CategoryTabs(label: 'Closed', tag: 3, id: 4)); return tabs; } + if (userType == UsersTypes.commissioningEngineer) { + tabs.add(CategoryTabs(label: 'Open', tag: 1, id: 2)); + tabs.add(CategoryTabs(label: 'In Progress', tag: 2, id: 3)); + tabs.add(CategoryTabs(label: 'Closed', tag: 3, id: 4)); + return tabs; + } tabs.add(CategoryTabs(label: 'Open', tag: 1, id: 2)); tabs.add(CategoryTabs(label: 'In Progress', tag: 2, id: 3)); diff --git a/lib/extensions/enum_extensions.dart b/lib/extensions/enum_extensions.dart index 747e2080..991a0f81 100644 --- a/lib/extensions/enum_extensions.dart +++ b/lib/extensions/enum_extensions.dart @@ -125,3 +125,9 @@ enum DropDownMatchType { identifier, name, } + + +enum CostCenterType { + costCenter, + serialNumber, +} diff --git a/lib/main.dart b/lib/main.dart index ceff381d..ed84e486 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -28,6 +28,7 @@ import 'package:test_sa/controllers/providers/api/status_drop_down/report/servic import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_report_repair_location_provider.dart'; import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_types_provider.dart'; import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart'; +import 'package:test_sa/modules/asset_delivery_module/provider/attachment_type_provider.dart'; import 'package:test_sa/modules/asset_delivery_module/provider/cost_center_provider.dart'; import 'package:test_sa/modules/asset_delivery_module/provider/end_user_rejection_reason_lookup_provider.dart'; import 'package:test_sa/modules/asset_delivery_module/provider/end_user_status_lookup_provider.dart'; @@ -326,6 +327,7 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => PowerSupplyTypeProvider()), ChangeNotifierProvider(create: (_) => EndUserStatusLookupProvider()), ChangeNotifierProvider(create: (_) => EndUserRejectionReasonLookupProvider()), + ChangeNotifierProvider(create: (_) => AttachmentTypeLookupProvider()), ], child: GestureDetector( onTap: () { diff --git a/lib/models/enums/user_types.dart b/lib/models/enums/user_types.dart index cd6b5f13..13238d83 100644 --- a/lib/models/enums/user_types.dart +++ b/lib/models/enums/user_types.dart @@ -4,5 +4,6 @@ enum UsersTypes { nurse, // 1 assessor, assessorTl, - qualityUser + qualityUser, + commissioningEngineer } diff --git a/lib/models/user.dart b/lib/models/user.dart index 5eac8486..a18b67c5 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -108,10 +108,13 @@ class User { return UsersTypes.assessor; case "R-19": // Head Nurse Role return UsersTypes.assessorTl; + //TODO need to confirm why repeat. case "R-3": // Quality User return UsersTypes.qualityUser; case "R-58": // Quality User return UsersTypes.qualityUser; + case "R-201": // Quality User + return UsersTypes.commissioningEngineer; default: return null; } diff --git a/lib/modules/asset_delivery_module/helper_function.dart b/lib/modules/asset_delivery_module/helper_function.dart new file mode 100644 index 00000000..b34a749c --- /dev/null +++ b/lib/modules/asset_delivery_module/helper_function.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/models/generic_attachment_model.dart'; +import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/attachment_view.dart'; +import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart'; +import 'package:test_sa/modules/asset_delivery_module/provider/attachment_type_provider.dart'; +import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; +import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; + +class HelperFunction { + static void attachmentTap({required BuildContext context, required AssetDeliveryProvider assetDeliveryProvider, required dynamic deliveryTableItemId}) async { + Lookup? result; + result = await showAttachmentTypeBottomSheet(context); + List list = []; + + if (result != null) { + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => const AppLazyLoading(), + ); + list = await assetDeliveryProvider.getAttachments(tableItemId: deliveryTableItemId, attachmentType: result.value); + Navigator.pop(context); + // if (list.isNotEmpty) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AssetDeliveryAttachmentView( + tableItemId: deliveryTableItemId, + attachmentType: result!.value, + attachmentList: list, + ))); + // } + } + } + + static Future showAttachmentTypeBottomSheet(BuildContext context) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + builder: (_) { + return SafeArea( + top: false, + child: Padding( + padding: const EdgeInsets.only( + top: 16, + left: 16, + right: 16, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SingleItemDropDownMenu( + context: context, + title: context.translation.reason, + onSelect: (value) { + Navigator.pop(context, value); + }, + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/modules/asset_delivery_module/models/cost_center_model.dart b/lib/modules/asset_delivery_module/models/cost_center_model.dart index 4bbfe9aa..0cacd34a 100644 --- a/lib/modules/asset_delivery_module/models/cost_center_model.dart +++ b/lib/modules/asset_delivery_module/models/cost_center_model.dart @@ -40,11 +40,6 @@ class CostCenterModel { data['id'] = id ?? 0; data['qty'] = quantity; - // data['costCenter'] = costCenter; - // data['acceptedBy'] = acceptedBy; - // data['acceptanceDate'] = acceptanceDate; - // data['acceptedByEmail'] = acceptedByEmail; - // data['acceptedBySignature'] = acceptedBySignature; data['costCenter'] = costCenterName; return data; } diff --git a/lib/modules/asset_delivery_module/models/cost_center_serial_no_form_model.dart b/lib/modules/asset_delivery_module/models/cost_center_serial_no_form_model.dart new file mode 100644 index 00000000..049abaa4 --- /dev/null +++ b/lib/modules/asset_delivery_module/models/cost_center_serial_no_form_model.dart @@ -0,0 +1,53 @@ +import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart'; + +class CostCenterSerialNoFormModel { + List? costCenters; + List? serialNos; + + CostCenterSerialNoFormModel({ + this.costCenters, + this.serialNos, + }); + + // From JSON + CostCenterSerialNoFormModel.fromJson(Map json) { + if (json['costCenters'] != null) { + costCenters = List.from((json['costCenters'] as List).map((e) => CostCenterModel.fromJson(e))); + } + + if (json['serialNos'] != null) { + serialNos = List.from((json['serialNos'] as List).map((e) => SerialNoModel.fromJson(e))); + } + } + + // To JSON + Map toJson() { + return { + 'costCenters': costCenters?.map((e) => e.toJson()).toList() ?? [], + 'serialNos': serialNos?.map((e) => e.toJson()).toList() ?? [], + }; + } +} + + +class SerialNoModel { + int? id; + String? serialNo; + SerialNoModel({ + this.id, + this.serialNo, + }); + SerialNoModel.fromJson(Map json) { + id = json['id'] ?? json['costCenterId']; + serialNo = json['serialNo']; + } + Map toJson() { + final Map data = {}; + data['id'] = id ?? 0; + data['serialNo'] = serialNo; + return data; + } +} + + + diff --git a/lib/modules/asset_delivery_module/models/delivery_inspection_form_model.dart b/lib/modules/asset_delivery_module/models/delivery_inspection_form_model.dart index 10b45d4f..4d73b496 100644 --- a/lib/modules/asset_delivery_module/models/delivery_inspection_form_model.dart +++ b/lib/modules/asset_delivery_module/models/delivery_inspection_form_model.dart @@ -1,6 +1,11 @@ +import 'package:test_sa/modules/asset_delivery_module/models/inspection_person_model.dart'; + class DeliveryInspectionFormModel { int? assetDeliveryExternalDeliveryId; String? deliveryInspectionInspectedBy; + InspectionPersonModel? inspectedByModel; + InspectionPersonModel? approvedByModel; + InspectionPersonModel? thirdApprovalModel; DateTime? deliveryInspectionInspectedDateFrom; DateTime? deliveryInspectionInspectedDateTo; num? totalTime; @@ -9,6 +14,9 @@ class DeliveryInspectionFormModel { DeliveryInspectionFormModel({ this.assetDeliveryExternalDeliveryId, this.deliveryInspectionInspectedBy, + this.inspectedByModel, + this.approvedByModel, + this.thirdApprovalModel, this.deliveryInspectionInspectedDateFrom, this.deliveryInspectionInspectedDateTo, this.totalTime, @@ -18,20 +26,32 @@ class DeliveryInspectionFormModel { DeliveryInspectionFormModel.fromJson(Map json) { assetDeliveryExternalDeliveryId = json['assetDeliveryExternalDeliveryId']; deliveryInspectionInspectedBy = json['deliveryInspectionInspectedBy']; - deliveryInspectionInspectedDateFrom = json['deliveryInspectionInspectedDateFrom']!=null?DateTime.tryParse(json['deliveryInspectionInspectedDateFrom']):null; - deliveryInspectionInspectedDateTo = json['deliveryInspectionInspectedDateTo']!=null?DateTime.tryParse(json['deliveryInspectionInspectedDateTo']):null; + inspectedByModel = InspectionPersonModel(name: json['deliveryInspectionInspectedBy'], email: json['deliveryInspectionInspectedByEmail'], signature: json['deliveryInspectionInspectedBySignature']); + approvedByModel = InspectionPersonModel(name: json['deliveryInspectionApprovedBy'], email: json['deliveryInspectionApprovedByEmail'], signature: json['deliveryInspectionApprovedBySignature']); + thirdApprovalModel = + InspectionPersonModel(name: json['deliveryInspectionThirdApproverBy'], email: json['deliveryInspectionThirdApproverByEmail'], signature: json['deliveryInspectionThirdApproverBySignature']); + deliveryInspectionInspectedDateFrom = json['deliveryInspectionInspectedDateFrom'] != null ? DateTime.tryParse(json['deliveryInspectionInspectedDateFrom']) : null; + deliveryInspectionInspectedDateTo = json['deliveryInspectionInspectedDateTo'] != null ? DateTime.tryParse(json['deliveryInspectionInspectedDateTo']) : null; totalTime = json['totalTime']; deliveryInspectionApprovedBy = json['deliveryInspectionApprovedBy']; } +//TODO need to set payload.. Map toJson() { return { 'assetDeliveryExternalDeliveryId': assetDeliveryExternalDeliveryId, - 'deliveryInspectionInspectedBy': deliveryInspectionInspectedBy, - 'deliveryInspectionInspectedDateFrom': deliveryInspectionInspectedDateFrom?.toIso8601String(), + 'deliveryInspectionInspectedDateFrom': deliveryInspectionInspectedDateFrom?.toIso8601String(), 'deliveryInspectionInspectedDateTo': deliveryInspectionInspectedDateTo?.toIso8601String(), 'totalTime': totalTime, - 'deliveryInspectionApprovedBy': deliveryInspectionApprovedBy, + 'deliveryInspectionInspectedBy': inspectedByModel?.name, + "deliveryInspectionInspectedBySignature": inspectedByModel?.signature, + "deliveryInspectionInspectedByEmail": inspectedByModel?.email, + 'deliveryInspectionApprovedBy': approvedByModel?.name, + "deliveryInspectionApprovedBySignature": approvedByModel?.signature, + "deliveryInspectionApprovedByEmail": approvedByModel?.email, + "deliveryInspectionThirdApproverBy": thirdApprovalModel?.name, + "deliveryInspectionThirdApproverBySignature": thirdApprovalModel?.signature, + "deliveryInspectionThirdApproverByEmail": thirdApprovalModel?.email, }; } } diff --git a/lib/modules/asset_delivery_module/models/inspection_person_model.dart b/lib/modules/asset_delivery_module/models/inspection_person_model.dart new file mode 100644 index 00000000..8c00357d --- /dev/null +++ b/lib/modules/asset_delivery_module/models/inspection_person_model.dart @@ -0,0 +1,15 @@ +import 'package:flutter/services.dart'; + +class InspectionPersonModel { + String? name; + String? email; + String? signature; + Uint8List? localSignature; + + InspectionPersonModel({ + this.name, + this.email, + this.signature, + this.localSignature, + }); +} diff --git a/lib/modules/asset_delivery_module/pages/attachment_view.dart b/lib/modules/asset_delivery_module/pages/attachment_view.dart index a8834fcc..e98ab0af 100644 --- a/lib/modules/asset_delivery_module/pages/attachment_view.dart +++ b/lib/modules/asset_delivery_module/pages/attachment_view.dart @@ -1,8 +1,10 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; @@ -15,21 +17,20 @@ 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_lazy_loading.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/views/widgets/images/files_list.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; class AssetDeliveryAttachmentView extends StatefulWidget { - ///Need to check which parm need here. - final int? lineId; final int? tableItemId; - final int? requestId; - final bool? isHmg; + final int? attachmentType; + final List attachmentList; const AssetDeliveryAttachmentView({ super.key, - this.lineId, - this.requestId, + required this.attachmentList, + this.attachmentType, this.tableItemId, - this.isHmg, }); @override @@ -52,15 +53,17 @@ class _AssetDeliveryAttachmentViewState extends State URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []).toShadowContainer( + context, + borderRadius: 20, + padding: 12, + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + ), + ], + ), + ), FooterActionButton.footerContainer( context: context, child: AppFilledButton( - buttonColor: AppColor.primary10, - label: 'Save'.addTranslation, - maxWidth: true, - onPressed: () async { - List attachement = []; - for (var item in _attachments) { - String fileName = - CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? ''; - attachement.add(GenericAttachmentModel(id: item.id ?? 0, name: fileName)); - } - showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); - Map payLoad = { - 'assetDeliveryExternalDeliveryId': widget.tableItemId, - 'attachments': attachement, - }; - await assetDeliveryProvider.saveAttachment(payload: payLoad).then((status) async { - Navigator.pop(context); - if (status) { - ///Need to verify need to stay on this page and refresh or go back.. - Navigator.pop(context); - // assetDeliveryProvider.getDeliveryLinesListById( - // itemId: widget.tableItemId, - // ); - } - }); - }), + buttonColor: AppColor.primary10, + label: 'Save'.addTranslation, + maxWidth: true, + onPressed: _onSavePressed, + ), ), ], ), ); } + + Future _onSavePressed() async { + final List attachmentPayload = _attachments.map((item) { + final String name = CMRequestUtils.isLocalUrl(item.name ?? '') ? '${item.name?.split("/").last}|${base64Encode(File(item.name!).readAsBytesSync())}' : item.name ?? ''; + + return GenericAttachmentModel( + id: item.id ?? 0, + name: name, + ); + }).toList(); + + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => const AppLazyLoading(), + ); + + final payload = { + 'assetDeliveryExternalDeliveryId': widget.tableItemId, + 'attachments': attachmentPayload, + }; + + final status = await assetDeliveryProvider.saveAttachment(payload: payload); + + Navigator.pop(context); + + if (status) { + Navigator.pop(context); + } + } } diff --git a/lib/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart b/lib/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart index 3cc0497b..2fdfe356 100644 --- a/lib/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; @@ -10,6 +8,7 @@ import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/cost_center_serial_no_form_model.dart'; import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart'; import 'package:test_sa/modules/asset_delivery_module/provider/cost_center_provider.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; @@ -47,8 +46,9 @@ class CostCenterFormView extends StatefulWidget { } class _CostCenterFormViewState extends State { - List _list = []; + CostCenterSerialNoFormModel formModel = CostCenterSerialNoFormModel(costCenters: [], serialNos: []); late AssetDeliveryProvider assetDeliveryProvider; + CostCenterType _selectedType = CostCenterType.costCenter; @override void initState() { @@ -60,15 +60,20 @@ class _CostCenterFormViewState extends State { } void getInitialData() async { - _list = await assetDeliveryProvider.getAssetDeliveryCostCenterListForLines( + formModel = await assetDeliveryProvider.getAssetDeliveryCostCenterListForLines( lineId: widget.lineId, tableItemId: widget.tableItemId, ); - if (_list.isEmpty) { - _list.add(CostCenterModel( + if (formModel.costCenters!.isEmpty) { + formModel.costCenters!.add(CostCenterModel( id: 0, )); } + // if (formModel.serialNos!.isEmpty) { + // formModel.serialNos!.add(SerialNoModel( + // id: 0, + // )); + // } if (widget.isHmg == true) { CostCenterProvider costCenterProvider = Provider.of(context, listen: false); if (costCenterProvider.items.isEmpty) { @@ -78,21 +83,21 @@ class _CostCenterFormViewState extends State { setState(() {}); } - void _addNewEntry() { + void addItem(List list, T Function() creator) { setState(() { - _list.add(CostCenterModel(costCenter: Lookup())); + list.add(creator()); }); } - void _removeEntry(int index) { + void removeItem(List list, int index) { setState(() { - _list.removeAt(index); + list.removeAt(index); }); } - void _updateModel(int index, void Function(CostCenterModel model) updateList) { + void updateItem(List list, int index, void Function(T item) updater) { setState(() { - updateList(_list[index]); + updater(list[index]); }); } @@ -111,34 +116,9 @@ class _CostCenterFormViewState extends State { ? const CircularProgressIndicator(color: AppColor.primary10).center : Column( children: [ - itemDetails(context: context, itemNo: widget.itemNo, itemDescription: widget.itemDescription), - ListView.builder( - itemCount: _list.length + 1, - shrinkWrap: true, - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8), - itemBuilder: (context, index) { - if (index == _list.length) { - return AppFilledButton( - label: "Add Cost Center".addTranslation, - maxWidth: true, - textColor: AppColor.textColor(context), - buttonColor: context.isDark ? AppColor.neutral60 : AppColor.white10, - icon: Icon(Icons.add_circle, color: AppColor.blueStatus(context)), - showIcon: true, - onPressed: _addNewEntry, - ); - } - return CostCenterItemCard( - model: _list[index], - index: index, - isReadOnly: false, - isHmg: widget.isHmg, - requestId: widget.requestId, - onUpdate: (updateList) => _updateModel(index, updateList), - onRemove: () => _removeEntry(index), - ); - }, - ).expanded, + itemDetails(), + selectionButton(), + _selectedType == CostCenterType.costCenter ? _buildCostCenterList() : _buildSerialNumberList(), FooterActionButton.footerContainer( context: context, child: AppFilledButton( @@ -147,7 +127,9 @@ class _CostCenterFormViewState extends State { maxWidth: true, onPressed: () async { showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); - await assetDeliveryProvider.saveCostCenterToLineDeliveryInspection(tableItemId: widget.tableItemId, lineId: widget.lineId, list: _list).then((status) async { + await assetDeliveryProvider + .saveCostCenterToLineDeliveryInspection(tableItemId: widget.tableItemId, lineId: widget.lineId, list: formModel.costCenters, serialNoList: formModel.serialNos) + .then((status) async { Navigator.pop(context); if (status) { Navigator.pop(context); @@ -163,40 +145,169 @@ class _CostCenterFormViewState extends State { }), ); } -} -Widget itemDetails({required BuildContext context, String? itemNo, String? itemDescription}) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Item No'.addTranslation, - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), - ), - 8.height, - Text( - itemNo ?? '-', - style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), - ), - 8.height, - Text( - 'Item Description'.addTranslation, - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + Widget _buildCostCenterList() { + return ListView.builder( + itemCount: formModel.costCenters!.length + 1, + shrinkWrap: true, + padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8), + itemBuilder: (context, index) { + if (index == formModel.costCenters!.length) { + return AppFilledButton( + label: "Add Cost Center".addTranslation, + maxWidth: true, + textColor: AppColor.textColor(context), + buttonColor: context.isDark ? AppColor.neutral60 : AppColor.white10, + icon: Icon(Icons.add_circle, color: AppColor.blueStatus(context)), + showIcon: true, + onPressed: () { + addItem(formModel.costCenters!, () => CostCenterModel(costCenter: Lookup())); + }, + ); + } + + return CostCenterItemCard( + model: formModel.costCenters![index], + index: index, + isReadOnly: false, + isHmg: widget.isHmg, + requestId: widget.requestId, + onUpdate: (update) { + updateItem(formModel.costCenters!, index, update); + }, + onRemove: () { + removeItem(formModel.costCenters!, index); + }, + ); + }, + ).expanded; + } + + Widget _buildSerialNumberList() { + return ListView.builder( + itemCount: formModel.serialNos!.length + 1, + shrinkWrap: true, + padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8), + itemBuilder: (context, index) { + if (index == formModel.serialNos!.length) { + return AppFilledButton( + label: "Add Serial Number".addTranslation, + maxWidth: true, + textColor: AppColor.textColor(context), + buttonColor: context.isDark ? AppColor.neutral60 : AppColor.white10, + icon: Icon(Icons.add_circle, color: AppColor.blueStatus(context)), + showIcon: true, + onPressed: () { + addItem(formModel.serialNos!, () => SerialNoModel()); + }, + ); + } + + return SerialNumberItemCard( + model: formModel.serialNos![index], + onUpdate: (update) { + updateItem(formModel.serialNos!, index, update); + }, + onRemove: () { + removeItem(formModel.serialNos!, index); + }, + ); + }, + ).expanded; + } + + Widget selectionButton() { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + children: [ + Expanded( + child: _radioItem( + value: CostCenterType.costCenter, + groupValue: _selectedType, + title: 'Cost Center', + onTap: () { + setState(() { + _selectedType = CostCenterType.costCenter; + }); + }, + ), + ), + Expanded( + child: _radioItem( + value: CostCenterType.serialNumber, + groupValue: _selectedType, + title: 'Serial Number', + onTap: () { + setState(() { + _selectedType = CostCenterType.serialNumber; + }); + }, + ), + ), + ], ), - 8.height, - Text( - itemDescription ?? '-', - style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ); + } + + Widget _radioItem({ + required CostCenterType value, + required CostCenterType groupValue, + required String title, + required VoidCallback onTap, + }) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: onTap, + child: Row( + children: [ + Radio( + value: value, + groupValue: groupValue, + onChanged: (_) => onTap(), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + visualDensity: VisualDensity.compact, + ), + 4.width, + title.addTranslation.bodyText(context), + ], ), - ], - ).toShadowContainer( - context, - borderRadius: 20, - padding: 12, - showShadow: false, - margin: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8), - ); + ); + } + + Widget itemDetails() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Item No'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + widget.itemNo ?? '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + Text( + 'Item Description'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + widget.itemDescription ?? '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + ], + ).toShadowContainer( + context, + borderRadius: 20, + padding: 12, + showShadow: false, + margin: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8), + ); + } } class CostCenterItemCard extends StatelessWidget { @@ -294,3 +405,53 @@ class CostCenterItemCard extends StatelessWidget { ).toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 12, vertical: 12)).paddingOnly(bottom: 12); } } + +class SerialNumberItemCard extends StatelessWidget { + final SerialNoModel model; + final VoidCallback onRemove; + final void Function(void Function(SerialNoModel model)) onUpdate; + + const SerialNumberItemCard({ + super.key, + required this.model, + required this.onRemove, + required this.onUpdate, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + 'Serial Number'.addTranslation.bodyText(context), + Container( + height: 28, + width: 28, + padding: const EdgeInsets.all(6), + child: "trash".toSvgAsset(height: 20, width: 20), + ).onPress(onRemove), + ], + ), + 8.height, + AppTextFormField( + labelText: 'Serial Number'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.serialNo ?? '', + textAlign: TextAlign.center, + showShadow: false, + onChange: (value) { + model.serialNo = value; + }, + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ) + .toShadowContainer( + context, + paddingObject: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + ) + .paddingOnly(bottom: 12); + } +} diff --git a/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart b/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart index 3ef8c0a0..c6918f74 100644 --- a/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart @@ -1,5 +1,5 @@ -import 'dart:developer'; - +import 'dart:convert'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; @@ -7,21 +7,26 @@ 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/generic_attachment_model.dart'; +import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/modules/asset_delivery_module/helper_function.dart'; import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart'; import 'package:test_sa/modules/asset_delivery_module/models/delivery_inspection_form_model.dart'; import 'package:test_sa/modules/asset_delivery_module/models/delivery_line_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/inspection_person_model.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_line_card_view.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/attachment_view.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart'; -import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/oracle_user_autocomplete_field.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart'; import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart'; +import 'package:test_sa/modules/asset_delivery_module/provider/attachment_type_provider.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; -import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; import '../../../../new_views/common_widgets/app_filled_button.dart'; @@ -129,7 +134,13 @@ class _DeliveryInspectionFormViewState extends State child: !viewOnly ? Column( children: [ - AppFilledButton(buttonColor: AppColor.primary10, label: 'Attachments'.addTranslation, maxWidth: true, onPressed: _attachmentTap), + AppFilledButton( + buttonColor: AppColor.primary10, + label: 'Attachments'.addTranslation, + maxWidth: true, + onPressed: () { + HelperFunction.attachmentTap(context: context, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id); + }), 16.height, Row( children: [ @@ -207,56 +218,109 @@ class _DeliveryInspectionFormViewState extends State Widget inspectionDetailsForm() { return Column( children: [ - if (widget.requestModel?.isHMG == true) ...[ - OracleUserAutoCompleteField( - clearAfterPick: false, - initialValue: model.deliveryInspectionInspectedBy ?? '', - label: 'Inspected by'.addTranslation, - onPick: (value) { - model.deliveryInspectionInspectedBy = value.fullName; - setState(() {}); - }, - onChanged: (value) {}, - ), - 8.height, - OracleUserAutoCompleteField( - clearAfterPick: false, - initialValue: model.deliveryInspectionApprovedBy ?? '', - label: 'Approved by'.addTranslation, - onChanged: (value) {}, - onPick: (value) { - model.deliveryInspectionApprovedBy = value.fullName; - setState(() {}); - }, - ), - ] //No HMG user free text insted of dropdown ... - else ...[ - AppTextFormField( - labelText: 'Inspected by'.addTranslation, - backgroundColor: AppColor.fieldBgColor(context), - initialValue: model.deliveryInspectionInspectedBy ?? '', - textAlign: TextAlign.center, - labelStyle: AppTextStyles.textFieldLabelStyle, - showShadow: false, - onChange: (value) { - model.deliveryInspectionInspectedBy = value; - }, - style: Theme.of(context).textTheme.titleMedium, - ), - 8.height, - AppTextFormField( - labelText: 'Approved by'.addTranslation, - backgroundColor: AppColor.fieldBgColor(context), - initialValue: model.deliveryInspectionApprovedBy ?? '', - textAlign: TextAlign.center, - labelStyle: AppTextStyles.textFieldLabelStyle, - showShadow: false, - onChange: (value) { - model.deliveryInspectionApprovedBy = value; + _inspectionTile( + title: 'Inspected by'.addTranslation, + model: model.inspectedByModel, + isHmg: widget.requestModel?.isHMG == true, + onTap: () async { + final result = await showInspectionBottomSheet( + context, + title: 'Inspected by'.addTranslation, + isHmg: widget.requestModel?.isHMG == true, + initialModel: model.inspectedByModel ?? InspectionPersonModel(), + ); + if (result != null) { + setState(() => model.inspectedByModel = result); + } + }, + ), + + 8.height, + _inspectionTile( + title: 'Approved by'.addTranslation, + model: model.approvedByModel, + isHmg: widget.requestModel?.isHMG == true, + onTap: () async { + final result = await showInspectionBottomSheet( + context, + title: 'Approved by'.addTranslation, + isHmg: widget.requestModel?.isHMG == true, + initialModel: model.approvedByModel ?? InspectionPersonModel(), + ); + if (result != null) { + setState(() => model.approvedByModel = result); + } + }, + ), + if (widget.requestModel?.paymentTerm?.value == 2) ...[ + _inspectionTile( + title: 'Third Approved by'.addTranslation, + model: model.thirdApprovalModel, + isHmg: widget.requestModel?.isHMG == true, + onTap: () async { + final result = await showInspectionBottomSheet( + context, + title: 'Third Approved by'.addTranslation, + isHmg: widget.requestModel?.isHMG == true, + initialModel: model.thirdApprovalModel ?? InspectionPersonModel(), + ); + if (result != null) { + setState(() => model.thirdApprovalModel = result); + } }, - style: Theme.of(context).textTheme.titleMedium, ), ], + + // if (widget.requestModel?.isHMG == true) ...[ + // OracleUserAutoCompleteField( + // clearAfterPick: false, + // initialValue: model.deliveryInspectionInspectedBy ?? '', + // label: 'Inspected by'.addTranslation, + // onPick: (value) { + // model.deliveryInspectionInspectedBy = value.fullName; + // setState(() {}); + // }, + // onChanged: (value) {}, + // ), + // 8.height, + // OracleUserAutoCompleteField( + // clearAfterPick: false, + // initialValue: model.deliveryInspectionApprovedBy ?? '', + // label: 'Approved by'.addTranslation, + // onChanged: (value) {}, + // onPick: (value) { + // model.deliveryInspectionApprovedBy = value.fullName; + // setState(() {}); + // }, + // ), + // ] //No HMG user free text insted of dropdown ... + // else ...[ + // AppTextFormField( + // labelText: 'Inspected by'.addTranslation, + // backgroundColor: AppColor.fieldBgColor(context), + // initialValue: model.deliveryInspectionInspectedBy ?? '', + // textAlign: TextAlign.center, + // labelStyle: AppTextStyles.textFieldLabelStyle, + // showShadow: false, + // onChange: (value) { + // model.deliveryInspectionInspectedBy = value; + // }, + // style: Theme.of(context).textTheme.titleMedium, + // ), + // 8.height, + // AppTextFormField( + // labelText: 'Approved by'.addTranslation, + // backgroundColor: AppColor.fieldBgColor(context), + // initialValue: model.deliveryInspectionApprovedBy ?? '', + // textAlign: TextAlign.center, + // labelStyle: AppTextStyles.textFieldLabelStyle, + // showShadow: false, + // onChange: (value) { + // model.deliveryInspectionApprovedBy = value; + // }, + // style: Theme.of(context).textTheme.titleMedium, + // ), + // ], 8.height, ADatePicker( label: 'Inspected Date/Time From'.addTranslation, @@ -417,13 +481,69 @@ class _DeliveryInspectionFormViewState extends State ); } - void _attachmentTap() async { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AssetDeliveryAttachmentView( - tableItemId: widget.deliveryTableModel.id, - ))); + Widget _inspectionTile({ + required String title, + InspectionPersonModel? model, + required bool isHmg, + required VoidCallback onTap, + }) { + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(12), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColor.fieldBgColor(context), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: AppTextStyles.textFieldLabelStyle), + 6.height, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + model?.name?.isNotEmpty == true ? model!.name! : 'Tap to add'.addTranslation, + style: AppTextStyles.bodyText, + ), + + ///Need to display other info as well + // Text( + // model?.email?.isNotEmpty == true + // ? model!.email! + // : '', + // style: AppTextStyles.bodyText, + // ), + ], + ), + ], + ), + ], + ), + ), + ); + } + + Future showAttachmentTypePicker() { + return showDialog( + context: context, + builder: (_) { + return AlertDialog( + content: SingleItemDropDownMenu( + context: context, + title: context.translation.reason, + showAsFullScreenDialog: false, + onSelect: (value) { + Navigator.pop(context, value); + }, + ), + ); + }, + ); } void _cancelTap() async { @@ -469,4 +589,40 @@ class _DeliveryInspectionFormViewState extends State } }); } + + Future showInspectionBottomSheet( + BuildContext context, { + required String title, + required bool isHmg, + required InspectionPersonModel initialModel, + }) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + builder: (_) { + if (initialModel.localSignature == null && initialModel.signature != null) { + initialModel.localSignature = signatureFromString(initialModel.signature); + } + return InspectionPersonBottomSheet( + title: title, + isHmg: isHmg, + model: InspectionPersonModel( + name: initialModel.name, + email: initialModel.email, + signature: initialModel.signature, + localSignature: initialModel.localSignature, + ), + ); + }, + ); + } + + Uint8List? signatureFromString(String? value) { + if (value == null || !value.contains('|')) return null; + final base64Part = value.split('|').last; + return base64Decode(base64Part); + } } diff --git a/lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart b/lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart new file mode 100644 index 00000000..bd81f92a --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart @@ -0,0 +1,152 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.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/modules/asset_delivery_module/models/inspection_person_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/oracle_user_autocomplete_field.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/views/widgets/e_signature/e_signature.dart'; + +class InspectionPersonBottomSheet extends StatefulWidget { + final String title; + bool isHmg; + final InspectionPersonModel model; + + InspectionPersonBottomSheet({ + super.key, + required this.title, + required this.isHmg, + required this.model, + }); + + @override + State createState() => _InspectionPersonBottomSheetState(); +} + +class _InspectionPersonBottomSheetState extends State { + late InspectionPersonModel _model; + + late TextEditingController nameController; + late TextEditingController emailController; + + @override + void initState() { + super.initState(); + + _model = InspectionPersonModel(name: widget.model.name, email: widget.model.email, signature: widget.model.signature, localSignature: widget.model.localSignature); + nameController = TextEditingController(text: _model.name ?? ''); + emailController = TextEditingController(text: _model.email ?? ''); + widget.isHmg = true; + log('isHmg = ${widget.isHmg}'); + } + + @override + void dispose() { + nameController.dispose(); + emailController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SafeArea( + top: false, + child: Padding( + padding: EdgeInsets.only( + top: 16, + left: 16, + right: 16, + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(widget.title, style: Theme.of(context).textTheme.titleLarge), + 16.height, + if (widget.isHmg) + OracleUserAutoCompleteField( + clearAfterPick: false, + initialValue: _model.name ?? '', + label: 'Name'.addTranslation, + onPick: (value) { + _model.name = value.fullName; + nameController.text = value.fullName ?? ''; + setState(() {}); + }, + ) + else + AppTextFormField( + labelText: 'Name'.addTranslation, + controller: nameController, + backgroundColor: AppColor.fieldBgColor(context), + showShadow: false, + textAlign: TextAlign.center, + labelStyle: AppTextStyles.textFieldLabelStyle, + style: Theme.of(context).textTheme.titleMedium, + onChange: (v) { + _model.name = v; + setState(() {}); + }, + ), + 12.height, + AppTextFormField( + labelText: 'Email'.addTranslation, + controller: emailController, + textInputType: TextInputType.emailAddress, + backgroundColor: AppColor.fieldBgColor(context), + showShadow: false, + textAlign: TextAlign.center, + labelStyle: AppTextStyles.textFieldLabelStyle, + style: Theme.of(context).textTheme.titleMedium, + onChange: (v) { + setState(() { + _model.email = v; + }); + }, + ), + 12.height, + ESignature( + title: '', + oldSignature: _model.localSignature == null ? _model.signature : null, + newSignature: _model.localSignature, + backgroundColor: AppColor.neutral100, + showShadow: false, + onChange: (signature) { + if (signature.isEmpty) return; + setState(() { + _model.localSignature = signature; + _model.signature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}"; + }); + }, + onSaved: (signature) { + if (signature == null || signature.isEmpty) return; + setState(() { + _model.localSignature = signature; + _model.signature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}"; + }); + }, + ), + 20.height, + AppFilledButton( + label: context.translation.save, + maxWidth: true, + buttonColor: AppColor.primary10, + onPressed: () async { + _model.name = nameController.text.trim(); + _model.email = emailController.text.trim(); + Navigator.pop(context, _model); + }, + ), + ], + ), + ), + ); + } +} diff --git a/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart b/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart index 5accb0ae..92979620 100644 --- a/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart @@ -5,6 +5,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/modules/asset_delivery_module/helper_function.dart'; import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart'; import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/attachment_view.dart'; @@ -156,12 +157,7 @@ class _EndUserCostCenterLIstViewState extends State { } void _attachmentTap() async { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AssetDeliveryAttachmentView( - tableItemId: widget.deliveryTableModel.id, - ))); + HelperFunction.attachmentTap(context: context, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id); } void _cancelTap() async { diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart index 11105480..b4c6d505 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart @@ -8,6 +8,7 @@ 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/lookup.dart'; +import 'package:test_sa/modules/asset_delivery_module/helper_function.dart'; import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart'; import 'package:test_sa/modules/asset_delivery_module/models/delivery_line_model.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_line_card_view.dart'; @@ -224,12 +225,13 @@ class _TechnicalInspectionLinesListViewState extends State AssetDeliveryAttachmentView( - tableItemId: widget.deliveryTableModel.id, - ))); + HelperFunction.attachmentTap(context: context, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id); + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => AssetDeliveryAttachmentView( + // tableItemId: widget.deliveryTableModel.id, + // ))); } void _cancelTap() async { diff --git a/lib/modules/asset_delivery_module/provider/asset_delivery_provider.dart b/lib/modules/asset_delivery_module/provider/asset_delivery_provider.dart index a9dedb0d..c01a4c2c 100644 --- a/lib/modules/asset_delivery_module/provider/asset_delivery_provider.dart +++ b/lib/modules/asset_delivery_module/provider/asset_delivery_provider.dart @@ -11,6 +11,7 @@ import 'package:test_sa/models/new_models/floor.dart'; import 'package:test_sa/models/new_models/room_model.dart'; import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart'; import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/cost_center_serial_no_form_model.dart'; import 'package:test_sa/modules/asset_delivery_module/models/delivery_inspection_form_model.dart'; import 'package:test_sa/modules/asset_delivery_module/models/delivery_line_model.dart'; import 'package:test_sa/modules/asset_delivery_module/models/end_user_asset_details_model.dart'; @@ -171,33 +172,38 @@ class AssetDeliveryProvider extends ChangeNotifier { } } - Future> getAssetDeliveryCostCenterListForLines({int? lineId, int? tableItemId}) async { - List costCenterList = []; + Future getAssetDeliveryCostCenterListForLines({ + int? lineId, + int? tableItemId, + }) async { + CostCenterSerialNoFormModel costCenterSerialNoFormModel = CostCenterSerialNoFormModel(); try { isLoading = true; notifyListeners(); + final response = await ApiManager.instance.get( - "${URLs.getCostCenterListToLineDeliveryInspection}?AssetDeliveryExternalDetailId=$lineId&AssetDeliveryExternalDeliveryId=$tableItemId", + "${URLs.getCostCenterAndSerialNoToLineDeliveryInspection}?AssetDeliveryExternalDetailId=$lineId&AssetDeliveryExternalDeliveryId=$tableItemId", ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { final jsonData = json.decode(response.body)["data"]; - if (jsonData is List) { - costCenterList = jsonData.map((e) => CostCenterModel.fromJson(e)).toList(); - } + costCenterSerialNoFormModel = CostCenterSerialNoFormModel.fromJson(jsonData); } else { - costCenterList = []; + costCenterSerialNoFormModel.costCenters = []; + costCenterSerialNoFormModel.serialNos = []; } - isLoading = false; - notifyListeners(); - return costCenterList; } catch (e) { log("get Delivery Lines List [error] : $e"); + costCenterSerialNoFormModel.costCenters = []; + costCenterSerialNoFormModel.serialNos = []; + } finally { isLoading = false; - costCenterList = []; notifyListeners(); - return costCenterList; } + + return costCenterSerialNoFormModel; } Future addNewDelivery({ @@ -253,15 +259,18 @@ class AssetDeliveryProvider extends ChangeNotifier { int? tableItemId, int? lineId, List? list, + List? serialNoList, }) async { isLoading = true; Map payload = { "assetDeliveryExternalDeliveryId": tableItemId, "assetDeliveryExternalDetailId": lineId, "costCenters": list?.map((x) => x.toJson()).toList(), + if (serialNoList != null && serialNoList.isNotEmpty) + "serialNos": serialNoList.map((x) => x.toJson()).toList(), }; try { - final response = await ApiManager.instance.post(URLs.saveCostCenterToLineDeliveryInspection, body: payload); + final response = await ApiManager.instance.post(URLs.saveCostCenterAndSerialNoToLineDeliveryInspection, body: payload); stateCode = response.statusCode; if (response.statusCode >= 200 && response.statusCode < 300) { isLoading = false; @@ -421,10 +430,11 @@ class AssetDeliveryProvider extends ChangeNotifier { Future> getAttachments({ num? tableItemId, + num? attachmentType, }) async { try { isLoading = true; - final response = await ApiManager.instance.get("${URLs.getAttachments}?assetDeliveryExternalDeliveryId=$tableItemId"); + final response = await ApiManager.instance.get("${URLs.getAttachments}?attachmentDocTypeValue=$attachmentType&assetDeliveryExternalDeliveryId=$tableItemId"); List list = []; if (response.statusCode >= 200 && response.statusCode < 300) { isLoading = false; diff --git a/lib/modules/asset_delivery_module/provider/attachment_type_provider.dart b/lib/modules/asset_delivery_module/provider/attachment_type_provider.dart new file mode 100644 index 00000000..2ef16d0b --- /dev/null +++ b/lib/modules/asset_delivery_module/provider/attachment_type_provider.dart @@ -0,0 +1,33 @@ +import 'dart:convert'; +import 'package:http/http.dart'; +import 'package:test_sa/controllers/api_routes/api_manager.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; + +class AttachmentTypeLookupProvider extends LoadingListNotifier { + @override + Future getData({int? id}) async { + if (loading == true) return -2; + loading = true; + notifyListeners(); + loading = true; + notifyListeners(); + try { + Response response = await ApiManager.instance.get(URLs.attachmentTypeLookup); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + 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/new_views/pages/land_page/my_request/all_requests_filter_page.dart b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart index ea9963d2..06482a0f 100644 --- a/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart +++ b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart @@ -106,6 +106,12 @@ class _AllRequestsFilterPageState extends State { types['Equipment Internal Audit'] = 10; types['System Internal Audit'] = 11; } + if (context.userProvider.isCommissioningEngineer) { + ///TODO need to check what other requests type needed here. + types = { + 'Asset Delivery': 13, + }; + } final statuses = { "All WO": 0, diff --git a/lib/new_views/pages/land_page/my_request/my_requests_page.dart b/lib/new_views/pages/land_page/my_request/my_requests_page.dart index 0a31b856..52c35338 100644 --- a/lib/new_views/pages/land_page/my_request/my_requests_page.dart +++ b/lib/new_views/pages/land_page/my_request/my_requests_page.dart @@ -69,7 +69,7 @@ class _MyRequestsPageState extends State { requestsList.add(Request(10, 'Equipment Internal Audit')); requestsList.add(Request(11, 'System Internal Audit')); //Need to be added for new user role commissioning engineer - requestsList.add(Request(13, "Asset Delivery")); + // requestsList.add(Request(13, "Asset Delivery")); } requestsList.add(Request(12, "Loan Equipment")); @@ -80,6 +80,13 @@ class _MyRequestsPageState extends State { Request(9, 'TRAF'), ]; } + if (context.userProvider.isCommissioningEngineer) { + ///TODO need to show only allowed ids for all workOrder + requestsList = [ + Request(null, context.translation.allWorkOrder), + Request(13, "Asset Delivery"), + ]; + } if (context.userProvider.isQualityUser) { requestsList = [ Request(null, context.translation.allWorkOrder), From ac50236aa261eba33e950e133908e7712d2a9433 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Thu, 15 Jan 2026 15:52:44 +0300 Subject: [PATCH 29/35] bugs fixes --- lib/controllers/api_routes/urls.dart | 8 +- .../widgets/request_category_list.dart | 3 + .../models/asset_delivery_data_model.dart | 6 +- .../technical_inspection_asset_model.dart | 2 +- .../delivery_inspection_form_view.dart | 219 +++++--- .../inpection_bottomsheet.dart | 6 +- .../end_user_asset_detail_card.dart | 10 +- .../update_end_user_asset_details_view.dart | 505 ++++++++++++++---- ...ate_end_user_cost_center_details_view.dart | 70 ++- .../update_child_asset_view.dart | 39 +- .../update_parent_asset_view.dart | 44 +- .../provider/asset_delivery_provider.dart | 3 +- 12 files changed, 676 insertions(+), 239 deletions(-) diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 2c8c2a6b..0930ace5 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -4,14 +4,14 @@ class URLs { static const String appReleaseBuildNumber = "31"; // static const host1 = "https://atomsm.hmg.com"; // production url - static const host1 = "https://atomsmdev.hmg.com"; // local DEV url - // static const host1 = "https://atomsmuat.hmg.com"; // local UAT url + // static const host1 = "https://atomsmdev.hmg.com"; // local DEV url + static const host1 = "https://atomsmuat.hmg.com"; // local UAT url // static const host1 = "http://10.201.111.125:9495"; // temporary Server UAT url // static String _baseUrl = "$_host/mobile"; - // static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis - static final String _baseUrl = "$_host/v6/mobile"; // for asset delivery module + static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis + // static final String _baseUrl = "$_host/v6/mobile"; // for asset delivery module // static final String _baseUrl = "$_host/mobile"; // host local UAT and for internal audit dev // static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM // static final String _baseUrl = "$_host/v5/mobile"; // v5 for data segregation diff --git a/lib/dashboard_latest/widgets/request_category_list.dart b/lib/dashboard_latest/widgets/request_category_list.dart index d0e5d156..9ae0e6ce 100644 --- a/lib/dashboard_latest/widgets/request_category_list.dart +++ b/lib/dashboard_latest/widgets/request_category_list.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/new_models/dashboard_detail.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_item_view.dart'; import 'package:test_sa/modules/asset_inventory_module/pages/inventory_session_item_view.dart'; import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_item_view.dart'; import 'package:test_sa/modules/tm_module/tasks/task_request_item_view.dart'; @@ -62,6 +63,8 @@ class RequestCategoryList extends StatelessWidget { return EquipmentInternalAuditItemView(requestData: request); case 11: return EquipmentInternalAuditItemView(requestData: request); + case 13: + return AssetDeliveryItemView(requestData: request); default: return Container( height: 100, diff --git a/lib/modules/asset_delivery_module/models/asset_delivery_data_model.dart b/lib/modules/asset_delivery_module/models/asset_delivery_data_model.dart index 08de3111..cde76346 100644 --- a/lib/modules/asset_delivery_module/models/asset_delivery_data_model.dart +++ b/lib/modules/asset_delivery_module/models/asset_delivery_data_model.dart @@ -241,7 +241,7 @@ class AssetDeliveryInternalDetail { num? qtyCancelled; num? unitPrice; num? unitPriceWithTax; - String? itemCategory; + Lookup? itemCategory; num? totalAssets; num? childPerAsset; @@ -259,7 +259,9 @@ class AssetDeliveryInternalDetail { qtyCancelled = json['qtyCancelled']; unitPrice = json['unitPrice']; unitPriceWithTax = json['unitPriceWithTax']; - itemCategory = json['itemCategory']; + itemCategory = json['itemCategory'] != null + ? Lookup.fromJson(json['itemCategory']) + : null; totalAssets = json['totalAssets']; childPerAsset = json['childPerAsset']; } diff --git a/lib/modules/asset_delivery_module/models/technical_inspection_asset_model.dart b/lib/modules/asset_delivery_module/models/technical_inspection_asset_model.dart index 64671b11..fafcc277 100644 --- a/lib/modules/asset_delivery_module/models/technical_inspection_asset_model.dart +++ b/lib/modules/asset_delivery_module/models/technical_inspection_asset_model.dart @@ -136,7 +136,7 @@ class TechnicalInspectionAssetModel { Map toChildJson() { final Map data = {}; - data['id'] = id; + data['id'] = id ?? 0; data['assetDeliveryInternalDetailAssetChildId'] = assetDeliveryInternalDetailAssetChildId; data['assetNumber'] = assetNumber; data['assetOriginId'] = assetOrigin?.id; diff --git a/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart b/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart index c6918f74..cdb4d5e9 100644 --- a/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart @@ -7,7 +7,6 @@ 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/generic_attachment_model.dart'; import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/modules/asset_delivery_module/helper_function.dart'; import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart'; @@ -15,7 +14,6 @@ import 'package:test_sa/modules/asset_delivery_module/models/delivery_inspection import 'package:test_sa/modules/asset_delivery_module/models/delivery_line_model.dart'; import 'package:test_sa/modules/asset_delivery_module/models/inspection_person_model.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_line_card_view.dart'; -import 'package:test_sa/modules/asset_delivery_module/pages/attachment_view.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart'; @@ -205,13 +203,22 @@ class _DeliveryInspectionFormViewState extends State } void calculateTotalTime() { - if (model.deliveryInspectionInspectedDateFrom == null || model.deliveryInspectionInspectedDateTo == null) { + final from = model.deliveryInspectionInspectedDateFrom; + final to = model.deliveryInspectionInspectedDateTo; + + if (from == null || to == null) { model.totalTime = null; return; } - final fromDate = model.deliveryInspectionInspectedDateFrom!; - final toDate = model.deliveryInspectionInspectedDateTo!; - final hours = toDate.difference(fromDate).inSeconds / 3600.0; + + final diffInSeconds = to.difference(from).inSeconds; + + if (diffInSeconds < 0) { + model.totalTime = null; + return; + } + + final hours = diffInSeconds / 3600; model.totalTime = num.parse(hours.toStringAsFixed(3)); } @@ -253,6 +260,7 @@ class _DeliveryInspectionFormViewState extends State }, ), if (widget.requestModel?.paymentTerm?.value == 2) ...[ + 8.height, _inspectionTile( title: 'Third Approved by'.addTranslation, model: model.thirdApprovalModel, @@ -330,52 +338,31 @@ class _DeliveryInspectionFormViewState extends State date: model.deliveryInspectionInspectedDateFrom, from: DateTime(DateTime.now().year, 1, 1), formatDateWithTime: true, - onDatePicker: (selectedDate) { - showTimePicker( + onDatePicker: (selectedDate) async { + final selectedTime = await showTimePicker( context: context, initialTime: TimeOfDay.now(), - builder: (BuildContext context, Widget? child) { - final ThemeData currentTheme = Theme.of(context); - return Theme( - data: currentTheme.copyWith( - timePickerTheme: TimePickerThemeData( - dialHandColor: AppColor.primary10, - dialBackgroundColor: Colors.grey.withOpacity(0.1), - hourMinuteColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.grey.withOpacity(0.1)), - dayPeriodColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.transparent), - dayPeriodTextColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? Colors.white : AppColor.primary10), - dayPeriodBorderSide: BorderSide(color: Colors.grey.withOpacity(0.2)), - entryModeIconColor: AppColor.primary10, - ), - textButtonTheme: TextButtonThemeData(style: TextButton.styleFrom(foregroundColor: AppColor.primary10)), - ), - child: child!, - ); - }, - ).then((selectedTime) { - if (selectedTime != null) { - model.deliveryInspectionInspectedDateFrom = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute); - // calculateTotalTime(); - - ///need to check if need validation or not. - // if (widget.pickerFromDate != null && _pickerStartAt!.isBefore(widget.pickerFromDate!)) { - // "Start time is before the request time.".showToast; - // _pickerStartAt = null; - // selectedTime = null; - // return; - // } - if (model.deliveryInspectionInspectedDateFrom!.isAfter(DateTime.now())) { - "Start time is after than current time".showToast; - model.deliveryInspectionInspectedDateFrom = null; - selectedTime = null; - return; - } - - setState(() {}); - } - }); + builder: _timePickerThemeBuilder, + ); + if (selectedTime == null) return; + + final fromDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + selectedTime.hour, + selectedTime.minute, + ); + + if (!_validateInspectionDates(from: fromDateTime, to: model.deliveryInspectionInspectedDateTo)) { + return; // invalid, do not assign + } + + model.deliveryInspectionInspectedDateFrom = fromDateTime; + setState(() {}); }, ), + 8.height, ADatePicker( label: 'Inspected Date/Time To'.addTranslation, @@ -385,55 +372,109 @@ class _DeliveryInspectionFormViewState extends State date: model.deliveryInspectionInspectedDateTo, from: model.deliveryInspectionInspectedDateFrom, formatDateWithTime: true, - onDatePicker: (selectedDate) { - showTimePicker( + onDatePicker: (selectedDate) async { + if (model.deliveryInspectionInspectedDateFrom == null) { + "Please select time to first".showToast; + return; + } + + final selectedTime = await showTimePicker( context: context, initialTime: TimeOfDay.now(), - builder: (BuildContext context, Widget? child) { - final ThemeData currentTheme = Theme.of(context); - return Theme( - data: currentTheme.copyWith( - timePickerTheme: TimePickerThemeData( - dialHandColor: AppColor.primary10, - dialBackgroundColor: Colors.grey.withOpacity(0.1), - hourMinuteColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.grey.withOpacity(0.1)), - dayPeriodColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.transparent), - dayPeriodTextColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? Colors.white : AppColor.primary10), - dayPeriodBorderSide: BorderSide(color: Colors.grey.withOpacity(0.2)), - entryModeIconColor: AppColor.primary10, - ), - textButtonTheme: TextButtonThemeData(style: TextButton.styleFrom(foregroundColor: AppColor.primary10)), - ), - child: child!, - ); - }, - ).then((selectedTime) { - if (selectedTime != null) { - model.deliveryInspectionInspectedDateTo = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute); - calculateTotalTime(); - - ///Need to check need validation or not.. - // if (widget.pickerFromDate != null && _pickerStartAt!.isBefore(widget.pickerFromDate!)) { - // "Start time is before the request time.".showToast; - // _pickerStartAt = null; - // selectedTime = null; - // return; - // } - if (model.deliveryInspectionInspectedDateTo!.isAfter(DateTime.now())) { - "End time is after than current time".showToast; - model.deliveryInspectionInspectedDateTo = null; - selectedTime = null; - return; - } - setState(() {}); - } - }); + builder: _timePickerThemeBuilder, + ); + if (selectedTime == null) return; + + final toDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + selectedTime.hour, + selectedTime.minute, + ); + + if (!_validateInspectionDates(from: model.deliveryInspectionInspectedDateFrom, to: toDateTime)) { + return; // invalid, do not assign + } + + model.deliveryInspectionInspectedDateTo = toDateTime; + setState(() {}); }, ), ], ); } + bool _validateInspectionDates({DateTime? from, DateTime? to}) { + if (from == null && to == null) return true; + if (from != null) { + if (from.isAfter(DateTime.now())) { + "Start time cannot be in the future".showToast; + return false; + } + if (to != null && from.isAfter(to)) { + "Start time cannot be after end time".showToast; + return false; + } + } + if (to != null) { + if (to.isAfter(DateTime.now())) { + "End time cannot be in the future".showToast; + return false; + } + if (from != null && to.isBefore(from)) { + "End time cannot be before start time".showToast; + return false; + } + } + + if (from != null && to != null) { + final diffInSeconds = to.difference(from).inSeconds; + if (diffInSeconds < 0) { + model.totalTime = null; + return false; + } + final hours = diffInSeconds / 3600.0; + model.totalTime = num.parse(hours.toStringAsFixed(3)); + } else { + model.totalTime = null; + } + + return true; + } + + Widget _timePickerThemeBuilder(BuildContext context, Widget? child) { + final ThemeData currentTheme = Theme.of(context); + + return Theme( + data: currentTheme.copyWith( + timePickerTheme: TimePickerThemeData( + dialHandColor: AppColor.primary10, + dialBackgroundColor: Colors.grey.withOpacity(0.1), + hourMinuteColor: MaterialStateColor.resolveWith( + (states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.grey.withOpacity(0.1), + ), + dayPeriodColor: MaterialStateColor.resolveWith( + (states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.transparent, + ), + dayPeriodTextColor: MaterialStateColor.resolveWith( + (states) => states.contains(MaterialState.selected) ? Colors.white : AppColor.primary10, + ), + dayPeriodBorderSide: BorderSide( + color: Colors.grey.withOpacity(0.2), + ), + entryModeIconColor: AppColor.primary10, + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: AppColor.primary10, + ), + ), + ), + child: child!, + ); + } + Widget inspectionDetails() { return Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart b/lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart index bd81f92a..106b3a97 100644 --- a/lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart @@ -15,10 +15,10 @@ import 'package:test_sa/views/widgets/e_signature/e_signature.dart'; class InspectionPersonBottomSheet extends StatefulWidget { final String title; - bool isHmg; + bool isHmg; final InspectionPersonModel model; - InspectionPersonBottomSheet({ + InspectionPersonBottomSheet({ super.key, required this.title, required this.isHmg, @@ -42,8 +42,6 @@ class _InspectionPersonBottomSheetState extends State createState() => _UpdateEndUserAssetDetailsViewState(); +// } +// +// class _UpdateEndUserAssetDetailsViewState extends State { +// final GlobalKey _formKey = GlobalKey(); +// final GlobalKey _scaffoldKey = GlobalKey(); +// bool viewOnly = false; +// late AssetDeliveryProvider assetDeliveryProvider; +// EndUserAssetDetailsModel model = EndUserAssetDetailsModel(); +// late TextEditingController quantityController; +// late TextEditingController descriptionController; +// +// @override +// void initState() { +// quantityController = TextEditingController(); +// descriptionController = TextEditingController(); +// WidgetsBinding.instance.addPostFrameCallback((_) { +// getInitialData(); +// }); +// super.initState(); +// } +// +// @override +// void dispose() { +// super.dispose(); +// } +// +// void getInitialData() async { +// assetDeliveryProvider = Provider.of(context, listen: false); +// if (widget.assetDetailsModel != null) { +// model = EndUserAssetDetailsModel.fromJson(widget.assetDetailsModel!.toJson()); +// model.costCenterName = widget.costCenterName; +// model.assetDeliveryExternalDeliveryId = widget.tableId; +// model.status = widget.assetDetailsModel?.status; +// model.rejectionReason = widget.assetDetailsModel?.rejectionReason; +// } +// quantityController.text = model.rejectedQty != null ? model.rejectedQty.toString() : ''; +// descriptionController.text = model.description ?? ''; +// model.type = 'Accessory'; +// setState(() {}); +// } +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// key: _scaffoldKey, +// appBar: DefaultAppBar( +// title: 'Item Details'.addTranslation, +// titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), +// ), +// body: Form( +// key: _formKey, +// child: Column( +// children: [ +// SingleChildScrollView( +// padding: const EdgeInsets.all(16), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// SingleItemDropDownMenu( +// context: context, +// height: 56.toScreenHeight, +// title: 'Status'.addTranslation, +// titleTextColor: AppColor.neutral120, +// showShadow: false, +// validator: (value) { +// if (value == null) return "Please select status"; +// return null; +// }, +// backgroundColor: AppColor.fieldBgColor(context), +// showAsBottomSheet: true, +// initialValue: model.status, +// onSelect: (status) { +// if (status != null) { +// model.status = status; +// setState(() {}); +// } +// }, +// ), +// 8.height, +// if (model.type == 'Accessory') ...[ +// AppTextFormField( +// labelText: 'Rejected Quantity'.addTranslation, +// controller: quantityController, +// backgroundColor: AppColor.fieldBgColor(context), +// textAlign: TextAlign.center, +// labelStyle: AppTextStyles.textFieldLabelStyle, +// textInputType: TextInputType.number, +// showShadow: false, +// validator: (value) { +// if ((value ?? "").isEmpty) return "Mandatory"; +// return null; +// }, +// onChange: (value) { +// if (value.isNotEmpty) { +// model.rejectedQty = model.rejectedQty = num.tryParse(value); +// setState(() {}); +// } +// }, +// style: Theme.of(context).textTheme.titleMedium, +// ), +// 8.height, +// if (model.status?.value == 1 && (model.rejectedQty != null && quantityController.text.isNotEmpty && model.rejectedQty! > 0)) +// SingleItemDropDownMenu( +// context: context, +// height: 56.toScreenHeight, +// title: 'Rejection Reason '.addTranslation, +// validator: (value) { +// if (value == null) return "Please select rejection reason"; +// return null; +// }, +// titleTextColor: AppColor.neutral120, +// showShadow: false, +// backgroundColor: AppColor.fieldBgColor(context), +// showAsBottomSheet: true, +// initialValue: model.rejectionReason, +// onSelect: (reason) { +// if (reason != null) { +// model.rejectionReason = reason; +// setState(() {}); +// } +// }, +// ), +// 8.height, +// ], +// if (model.status != null && model.status!.value == 2) ...[ +// SingleItemDropDownMenu( +// context: context, +// height: 56.toScreenHeight, +// title: 'Rejection Reason '.addTranslation, +// validator: (value) { +// if (value == null) return "Please select rejection reason"; +// return null; +// }, +// titleTextColor: AppColor.neutral120, +// showShadow: false, +// backgroundColor: AppColor.fieldBgColor(context), +// showAsBottomSheet: true, +// initialValue: model.rejectionReason, +// onSelect: (reason) { +// if (reason != null) { +// model.rejectionReason = reason; +// setState(() {}); +// } +// }, +// ), +// 8.height, +// ], +// AppTextFormField( +// controller: descriptionController, +// labelText: 'Description'.addTranslation, +// backgroundColor: AppColor.fieldBgColor(context), +// showShadow: false, +// labelStyle: AppTextStyles.textFieldLabelStyle, +// alignLabelWithHint: true, +// textInputType: TextInputType.multiline, +// onChange: (value) { +// model.description = value; +// }, +// onSaved: (value) { +// model.description = value; +// }, +// ), +// 8.height, +// ], +// ).toShadowContainer(context, borderRadius: 20, padding: 12)) +// .expanded, +// FooterActionButton.footerContainer(context: context, child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Save'.addTranslation, maxWidth: true, onPressed: _saveTap)), +// ], +// ), +// ), +// ); +// } +// +// ///Need to check which details show here... +// +// void _saveTap() async { +// if (!_formKey.currentState!.validate()) { +// return; +// } +// _formKey.currentState!.save(); +// log('payload ${model.toJson()}'); +// return; +// showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); +// await assetDeliveryProvider.saveEndUserAssetDataData(model: model).then((status) async { +// Navigator.pop(context); +// if (status) { +// Navigator.pop(context, true); +// } +// }); +// } +// } + import 'dart:developer'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; @@ -22,11 +245,16 @@ import '../../../../new_views/common_widgets/app_filled_button.dart'; import '../../../../new_views/common_widgets/default_app_bar.dart'; class UpdateEndUserAssetDetailsView extends StatefulWidget { - EndUserAssetDetailsModel? assetDetailsModel; - String? costCenterName; - int? tableId; + final EndUserAssetDetailsModel? assetDetailsModel; + final String? costCenterName; + final int? tableId; - UpdateEndUserAssetDetailsView({Key? key, this.assetDetailsModel, this.costCenterName, this.tableId}) : super(key: key); + const UpdateEndUserAssetDetailsView({ + Key? key, + this.assetDetailsModel, + this.costCenterName, + this.tableId, + }) : super(key: key); @override State createState() => _UpdateEndUserAssetDetailsViewState(); @@ -34,147 +262,220 @@ class UpdateEndUserAssetDetailsView extends StatefulWidget { class _UpdateEndUserAssetDetailsViewState extends State { final GlobalKey _formKey = GlobalKey(); - final GlobalKey _scaffoldKey = GlobalKey(); - bool viewOnly = false; + late AssetDeliveryProvider assetDeliveryProvider; EndUserAssetDetailsModel model = EndUserAssetDetailsModel(); + late TextEditingController quantityController; late TextEditingController descriptionController; @override void initState() { + super.initState(); quantityController = TextEditingController(); descriptionController = TextEditingController(); + WidgetsBinding.instance.addPostFrameCallback((_) { - getInitialData(); + _loadInitialData(); }); - super.initState(); } - @override - void dispose() { - super.dispose(); - } - - void getInitialData() async { + void _loadInitialData() { assetDeliveryProvider = Provider.of(context, listen: false); + if (widget.assetDetailsModel != null) { - model = EndUserAssetDetailsModel.fromJson(widget.assetDetailsModel!.toJson()); + model = EndUserAssetDetailsModel.fromJson( + widget.assetDetailsModel!.toJson(), + ); model.costCenterName = widget.costCenterName; model.assetDeliveryExternalDeliveryId = widget.tableId; model.status = widget.assetDetailsModel?.status; model.rejectionReason = widget.assetDetailsModel?.rejectionReason; } + quantityController.text = model.rejectedQty != null ? model.rejectedQty.toString() : ''; descriptionController.text = model.description ?? ''; setState(() {}); } + bool get _isAccessory => model.type == 'Accessory'; + + bool get _isAccepted => model.status?.value == 1; + + bool get _isRejected => model.status?.value == 2; + + bool get _showRejectedQty { + if (_isAccessory) return true; + return false; + } + + bool get _showRejectionReason { + if (_isAccessory) { + if (_isRejected) return true; + if (_isAccepted && (model.rejectedQty ?? 0) > 0) return true; + return false; + } + if (_isRejected) return true; + return false; + } + @override Widget build(BuildContext context) { return Scaffold( - key: _scaffoldKey, appBar: DefaultAppBar( title: 'Item Details'.addTranslation, - titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + titleStyle: AppTextStyles.heading3.copyWith( + fontWeight: FontWeight.w500, + color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, + ), ), body: Form( key: _formKey, child: Column( children: [ SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SingleItemDropDownMenu( - context: context, - height: 56.toScreenHeight, - title: 'Status'.addTranslation, - titleTextColor: AppColor.neutral120, - showShadow: false, - backgroundColor: AppColor.fieldBgColor(context), - showAsBottomSheet: true, - initialValue: model.status, - onSelect: (status) { - if (status != null) { - model.status = status; - setState(() {}); - } - }, - ), - 8.height, - SingleItemDropDownMenu( - context: context, - height: 56.toScreenHeight, - title: 'Rejection Reason '.addTranslation, - titleTextColor: AppColor.neutral120, - showShadow: false, - backgroundColor: AppColor.fieldBgColor(context), - showAsBottomSheet: true, - initialValue: model.rejectionReason, - onSelect: (reason) { - if (reason != null) { - model.rejectionReason = reason; - setState(() {}); - } - }, - ), - 8.height, - if (model.type == 'Accessory') ...[ - AppTextFormField( - labelText: 'Rejected Quantity'.addTranslation, - controller: quantityController, - backgroundColor: AppColor.fieldBgColor(context), - textAlign: TextAlign.center, - labelStyle: AppTextStyles.textFieldLabelStyle, - textInputType: TextInputType.number, - showShadow: false, - onChange: (value) { - if (value.isNotEmpty) { - model.rejectedQty = model.rejectedQty = num.tryParse(value); - } - }, - style: Theme.of(context).textTheme.titleMedium, - ), - 8.height, - ], - AppTextFormField( - controller: descriptionController, - labelText: 'Description'.addTranslation, - backgroundColor: AppColor.fieldBgColor(context), - showShadow: false, - labelStyle: AppTextStyles.textFieldLabelStyle, - alignLabelWithHint: true, - textInputType: TextInputType.multiline, - onChange: (value) { - model.description = value; - }, - onSaved: (value) { - model.description = value; - }, - ), - 8.height, - ], - ).toShadowContainer(context, borderRadius: 20, padding: 12)) - .expanded, - FooterActionButton.footerContainer(context: context, child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Save'.addTranslation, maxWidth: true, onPressed: _saveTap)), + padding: const EdgeInsets.all(16), + child: _formBody(context), + ).expanded, + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + buttonColor: AppColor.primary10, + label: 'Save'.addTranslation, + maxWidth: true, + onPressed: _saveTap, + ), + ), ], ), ), ); } - ///Need to check which details show here... + Widget _formBody(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _statusDropdown(), + 8.height, + if (_showRejectedQty) ...[ + _rejectedQtyField(), + 8.height, + ], + if (_showRejectionReason) ...[ + _rejectionReasonDropdown(), + 8.height, + ], + _descriptionField(), + 8.height, + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12); + } + + Widget _statusDropdown() { + return SingleItemDropDownMenu( + context: context, + height: 56.toScreenHeight, + title: 'Status'.addTranslation, + titleTextColor: AppColor.neutral120, + showShadow: false, + backgroundColor: AppColor.fieldBgColor(context), + showAsBottomSheet: true, + initialValue: model.status, + validator: (value) { + if (value == null) return 'Please select status'; + return null; + }, + onSelect: (status) { + if (status != null) { + model.status = status; + model.rejectionReason = null; + setState(() {}); + } + }, + ); + } + + Widget _rejectedQtyField() { + return AppTextFormField( + labelText: 'Rejected Quantity'.addTranslation, + controller: quantityController, + backgroundColor: AppColor.fieldBgColor(context), + textAlign: TextAlign.center, + labelStyle: AppTextStyles.textFieldLabelStyle, + textInputType: TextInputType.number, + showShadow: false, + style: Theme.of(context).textTheme.titleMedium, + validator: (value) { + if (!_showRejectedQty) return null; + if ((value ?? '').isEmpty) return 'Mandatory'; + final qty = num.tryParse(value!); + if (qty == null || qty <= 0) return 'Enter valid quantity'; + return null; + }, + onChange: (value) { + model.rejectedQty = value.isNotEmpty ? num.tryParse(value) : null; + setState(() {}); + }, + ); + } + + Widget _rejectionReasonDropdown() { + return SingleItemDropDownMenu( + context: context, + height: 56.toScreenHeight, + title: 'Rejection Reason'.addTranslation, + titleTextColor: AppColor.neutral120, + showShadow: false, + backgroundColor: AppColor.fieldBgColor(context), + showAsBottomSheet: true, + initialValue: model.rejectionReason, + validator: (value) { + if (!_showRejectionReason) return null; + if (value == null) return 'Please select rejection reason'; + return null; + }, + onSelect: (reason) { + if (reason != null) { + model.rejectionReason = reason; + setState(() {}); + } + }, + ); + } + + Widget _descriptionField() { + return AppTextFormField( + controller: descriptionController, + labelText: 'Description'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + showShadow: false, + labelStyle: AppTextStyles.textFieldLabelStyle, + alignLabelWithHint: true, + textInputType: TextInputType.multiline, + onChange: (value) => model.description = value, + onSaved: (value) => model.description = value, + ); + } void _saveTap() async { + if (!_formKey.currentState!.validate()) return; + _formKey.currentState!.save(); - showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); - await assetDeliveryProvider.saveEndUserAssetDataData(model: model).then((status) async { - Navigator.pop(context); - if (status) { - Navigator.pop(context, true); - } - }); + log('payload ${model.toJson()}'); + + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => const AppLazyLoading(), + ); + + final status = await assetDeliveryProvider.saveEndUserAssetDataData(model: model); + + Navigator.pop(context); + + if (status) { + Navigator.pop(context, true); + } } } diff --git a/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_cost_center_details_view.dart b/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_cost_center_details_view.dart index 3b98beaa..1234a018 100644 --- a/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_cost_center_details_view.dart +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_cost_center_details_view.dart @@ -235,16 +235,6 @@ class _UpdateEndUserCostCenterDetailsViewState extends State const AppLazyLoading()); model.assetDeliveryExternalDetailId = widget.tableItemId; diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart index bb0b6ca9..be67dd74 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart @@ -189,6 +189,39 @@ class _UpdateChildAssetViewState extends State { } }), 8.height, + AppTextFormField( + labelText: 'Asset Name'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.modelDefinition?.assetName, + textAlign: TextAlign.center, + enable: false, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: 'Model'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.modelDefinition?.model, + textAlign: TextAlign.center, + enable: false, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: 'Manufacturer'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.modelDefinition?.manufacturer ?? '', + textAlign: TextAlign.center, + enable: false, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, AppTextFormField( labelText: 'Serial Number'.addTranslation, backgroundColor: AppColor.fieldBgColor(context), @@ -423,6 +456,10 @@ class _UpdateChildAssetViewState extends State { } String _statusFromBool(bool? value) { - return value == true ? 'PASS' :value==false? 'FAIL':''; + return value == true + ? 'PASS' + : value == false + ? 'FAIL' + : ''; } } diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart index 8bac4319..52a14cda 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart @@ -216,15 +216,55 @@ class _UpdateParentAssetViewState extends State { if (assetModel.site != null) { await provider.getSiteData(siteId: assetModel.site?.id, model: provider.parentFormModel); } - assetModel.assetDeliveryInternalDetailAssetId = assetModel.id; - assetModel.id = 0; + assetModel.assetDeliveryInternalDetailAssetId = assetModel.id; + assetModel.id = 0; + if (assetModel.childAssetList != null && assetModel.childAssetList!.isNotEmpty) { + for (var asset in assetModel.childAssetList!) { + asset.assetDeliveryInternalDetailAssetChildId = asset.id; + asset.id = 0; + } + } provider.setParentModel(assetModel); log('child list length ${assetModel.childAssetList?.length}'); + populateTextFields(model: provider.parentFormModel); } } }), 8.height, + AppTextFormField( + labelText: 'Asset Name'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.modelDefinition?.assetName, + textAlign: TextAlign.center, + enable: false, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: 'Model'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.modelDefinition?.model, + textAlign: TextAlign.center, + enable: false, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: 'Manufacturer'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.modelDefinition?.manufacturer ?? '', + textAlign: TextAlign.center, + enable: false, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, AppTextFormField( labelText: 'Serial Number'.addTranslation, backgroundColor: AppColor.fieldBgColor(context), diff --git a/lib/modules/asset_delivery_module/provider/asset_delivery_provider.dart b/lib/modules/asset_delivery_module/provider/asset_delivery_provider.dart index c01a4c2c..6516b500 100644 --- a/lib/modules/asset_delivery_module/provider/asset_delivery_provider.dart +++ b/lib/modules/asset_delivery_module/provider/asset_delivery_provider.dart @@ -266,8 +266,7 @@ class AssetDeliveryProvider extends ChangeNotifier { "assetDeliveryExternalDeliveryId": tableItemId, "assetDeliveryExternalDetailId": lineId, "costCenters": list?.map((x) => x.toJson()).toList(), - if (serialNoList != null && serialNoList.isNotEmpty) - "serialNos": serialNoList.map((x) => x.toJson()).toList(), + if (serialNoList != null && serialNoList.isNotEmpty) "serialNos": serialNoList.map((x) => x.toJson()).toList(), }; try { final response = await ApiManager.instance.post(URLs.saveCostCenterAndSerialNoToLineDeliveryInspection, body: payload); From b78d10af468cc3e6e4442f2f54f6a182b1249d10 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 15 Jan 2026 18:20:38 +0300 Subject: [PATCH 30/35] current user employee id for chat. --- lib/models/user.dart | 4 ++++ .../cm_module/views/service_request_detail_main_view.dart | 2 +- lib/modules/tm_module/tasks_wo/task_request_detail_view.dart | 4 ++-- lib/views/pages/device_transfer/device_transfer_details.dart | 4 ++-- lib/views/pages/user/gas_refill/gas_refill_details.dart | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/models/user.dart b/lib/models/user.dart index 5eac8486..747f221b 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -12,6 +12,7 @@ class User { List? departmentName; String? message; String? username; + String? employeeId; String? userID; String? email; String? password; @@ -53,6 +54,7 @@ class User { this.departmentName, this.message, this.username, + this.employeeId, this.userID, this.email, this.password, @@ -148,6 +150,7 @@ class User { map['message'] = message; map['username'] = username; map['userID'] = userID; + map['employeeId'] = employeeId; map['email'] = email; map['password'] = password; map['token'] = token; @@ -209,6 +212,7 @@ class User { message = json['message']; username = json['username']; userID = json['userID']; + employeeId = json['employeeId']; email = json['email']; password = json['password']; token = json['token']; diff --git a/lib/modules/cm_module/views/service_request_detail_main_view.dart b/lib/modules/cm_module/views/service_request_detail_main_view.dart index f59b49e1..36d5e8bf 100644 --- a/lib/modules/cm_module/views/service_request_detail_main_view.dart +++ b/lib/modules/cm_module/views/service_request_detail_main_view.dart @@ -106,7 +106,7 @@ class _ServiceRequestDetailMainState extends State { isReadOnly: _requestProvider.isReadOnlyRequest, requestId: widget.requestId.toInt(), assigneeEmployeeNumber: provider.currentWorkOrder?.data?.assignedEmployee!.employeeId!, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: provider.currentWorkOrder?.data?.workOrderContactPerson.first.employeeId, ); } diff --git a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart index bf3aa96f..514d8055 100644 --- a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart @@ -80,11 +80,11 @@ class _TaskRequestDetailsViewState extends State { (_taskReqModel.taskJobContactPersons?.isNotEmpty ?? false)) { return ChatWidget( moduleId: widget.moduleId, - isShow: context.settingProvider.isUserFlowMedical&& _taskReqModel.taskJobStatus!.value! != 1, + isShow: context.settingProvider.isUserFlowMedical && _taskReqModel.taskJobStatus!.value! != 1, isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, requestId: widget.taskId.toInt(), assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId, ); } else diff --git a/lib/views/pages/device_transfer/device_transfer_details.dart b/lib/views/pages/device_transfer/device_transfer_details.dart index e0ad05bc..49b58057 100644 --- a/lib/views/pages/device_transfer/device_transfer_details.dart +++ b/lib/views/pages/device_transfer/device_transfer_details.dart @@ -227,7 +227,7 @@ class _DeviceTransferDetailsState extends State { isReadOnly: _model!.senderMachineStatusValue! != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: _model!.senderAssignedEmployeeNumber, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: _model!.assetTransferContactPersons!.first.employeeNumber, ), if ((_userProvider!.user?.type == UsersTypes.engineer)) @@ -288,7 +288,7 @@ class _DeviceTransferDetailsState extends State { isReadOnly: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue) != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: isSender ? _deviceTransfer.senderAssignedEmployeeNumber! : _deviceTransfer.receiverAssignedEmployeeNumber!, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: _deviceTransfer.assetTransferContactPersons!.first.employeeNumber, ), if ((_userProvider!.user?.type == UsersTypes.engineer)) diff --git a/lib/views/pages/user/gas_refill/gas_refill_details.dart b/lib/views/pages/user/gas_refill/gas_refill_details.dart index 74f4287e..45d3c128 100644 --- a/lib/views/pages/user/gas_refill/gas_refill_details.dart +++ b/lib/views/pages/user/gas_refill/gas_refill_details.dart @@ -72,7 +72,7 @@ class _GasRefillDetailsPageState extends State { isReadOnly: _gasRefillModel.status!.value! != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: _gasRefillModel.assignedEmployee?.employeeId!, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: _gasRefillModel.gasRefillContactPerson!.first.employeeCode!, ); }) From 415bdf18f198231cfd426f42c72bd08ef69a414b Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Thu, 15 Jan 2026 18:31:01 +0300 Subject: [PATCH 31/35] improvements --- .../update_end_user_asset_details_view.dart | 42 ++++++++++++++++++- .../update_child_asset_view.dart | 6 +-- .../update_parent_asset_view.dart | 6 +-- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart b/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart index e760abca..2918d5b2 100644 --- a/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart @@ -282,7 +282,6 @@ class _UpdateEndUserAssetDetailsViewState extends State(context, listen: false); - if (widget.assetDetailsModel != null) { model = EndUserAssetDetailsModel.fromJson( widget.assetDetailsModel!.toJson(), @@ -291,7 +290,9 @@ class _UpdateEndUserAssetDetailsViewState extends State( context: context, diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart index be67dd74..9bbc97cb 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart @@ -191,7 +191,7 @@ class _UpdateChildAssetViewState extends State { 8.height, AppTextFormField( labelText: 'Asset Name'.addTranslation, - backgroundColor: AppColor.fieldBgColor(context), + // backgroundColor: AppColor.fieldBgColor(context), initialValue: model.modelDefinition?.assetName, textAlign: TextAlign.center, enable: false, @@ -202,7 +202,7 @@ class _UpdateChildAssetViewState extends State { 8.height, AppTextFormField( labelText: 'Model'.addTranslation, - backgroundColor: AppColor.fieldBgColor(context), + // backgroundColor: AppColor.fieldBgColor(context), initialValue: model.modelDefinition?.model, textAlign: TextAlign.center, enable: false, @@ -213,7 +213,7 @@ class _UpdateChildAssetViewState extends State { 8.height, AppTextFormField( labelText: 'Manufacturer'.addTranslation, - backgroundColor: AppColor.fieldBgColor(context), + // backgroundColor: AppColor.fieldBgColor(context), initialValue: model.modelDefinition?.manufacturer ?? '', textAlign: TextAlign.center, enable: false, diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart index 52a14cda..024dfe5f 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart @@ -234,7 +234,7 @@ class _UpdateParentAssetViewState extends State { 8.height, AppTextFormField( labelText: 'Asset Name'.addTranslation, - backgroundColor: AppColor.fieldBgColor(context), + // backgroundColor: AppColor.fieldBgColor(context), initialValue: model.modelDefinition?.assetName, textAlign: TextAlign.center, enable: false, @@ -245,7 +245,7 @@ class _UpdateParentAssetViewState extends State { 8.height, AppTextFormField( labelText: 'Model'.addTranslation, - backgroundColor: AppColor.fieldBgColor(context), + // backgroundColor: AppColor.fieldBgColor(context), initialValue: model.modelDefinition?.model, textAlign: TextAlign.center, enable: false, @@ -256,7 +256,7 @@ class _UpdateParentAssetViewState extends State { 8.height, AppTextFormField( labelText: 'Manufacturer'.addTranslation, - backgroundColor: AppColor.fieldBgColor(context), + // backgroundColor: AppColor.fieldBgColor(context), initialValue: model.modelDefinition?.manufacturer ?? '', textAlign: TextAlign.center, enable: false, From 73ee12cba4a04de386cf6e0afd03e9a0a4aa59b6 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Sun, 18 Jan 2026 15:02:53 +0300 Subject: [PATCH 32/35] Atoms-1743,1745,1748 fixes --- .../asset_delivery_utils.dart | 0 .../delivery_inspection_form_view.dart | 30 +++++++++++++++---- .../inpection_bottomsheet.dart | 2 ++ .../oracle_user_autocomplete_field.dart | 27 +++++++++-------- .../update_end_user_asset_details_view.dart | 6 ++-- .../status_checklist_view.dart | 2 +- .../technical_inpection_asset_card_view.dart | 8 +++++ .../update_child_asset_view.dart | 12 +++++++- .../update_parent_asset_view.dart | 11 ++++++- ...pdate_technical_inspection_lines_view.dart | 21 +++++++++++++ .../common_widgets/app_bottom_nav_bar.dart | 5 ++-- 11 files changed, 97 insertions(+), 27 deletions(-) rename lib/modules/asset_delivery_module/pages/{technical_inpection => }/asset_delivery_utils.dart (100%) diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/asset_delivery_utils.dart b/lib/modules/asset_delivery_module/pages/asset_delivery_utils.dart similarity index 100% rename from lib/modules/asset_delivery_module/pages/technical_inpection/asset_delivery_utils.dart rename to lib/modules/asset_delivery_module/pages/asset_delivery_utils.dart diff --git a/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart b/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart index cdb4d5e9..9d589023 100644 --- a/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart @@ -113,9 +113,16 @@ class _DeliveryInspectionFormViewState extends State ), 8.height, Text( - model.totalTime != null ? '${model.totalTime.toString()} hours' : '-', - style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + formatInspectionDuration(model.totalTime), + style: AppTextStyles.bodyText2.copyWith( + color: context.isDark ? AppColor.neutral30 : AppColor.neutral120, + ), ), + + // Text( + // model.totalTime != null ? '${model.totalTime.toString()} hours' : '-', + // style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + // ), 8.height, const Divider().defaultStyle(context), Text( @@ -409,21 +416,21 @@ class _DeliveryInspectionFormViewState extends State if (from == null && to == null) return true; if (from != null) { if (from.isAfter(DateTime.now())) { - "Start time cannot be in the future".showToast; + "Time From cannot be in the future".showToast; return false; } if (to != null && from.isAfter(to)) { - "Start time cannot be after end time".showToast; + "Time To cannot be after Time From".showToast; return false; } } if (to != null) { if (to.isAfter(DateTime.now())) { - "End time cannot be in the future".showToast; + "Time To cannot be in the future".showToast; return false; } if (from != null && to.isBefore(from)) { - "End time cannot be before start time".showToast; + "Timer To cannot be before from time".showToast; return false; } } @@ -443,6 +450,17 @@ class _DeliveryInspectionFormViewState extends State return true; } + String formatInspectionDuration(num? hours) { + if (hours == null) return '-'; + + if (hours < 1) { + final minutes = (hours * 60).round(); + return '$minutes minutes'; + } + + return '${hours.toStringAsFixed(2)} hours'; + } + Widget _timePickerThemeBuilder(BuildContext context, Widget? child) { final ThemeData currentTheme = Theme.of(context); diff --git a/lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart b/lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart index 106b3a97..434f3382 100644 --- a/lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart @@ -76,6 +76,7 @@ class _InspectionPersonBottomSheetState extends State { AssetDeliveryProvider? assetDeliveryProvider; - late TextEditingController _controller; + + // late TextEditingController _controller; @override void initState() { - _controller = TextEditingController(text: widget.initialValue); + // _controller = TextEditingController(text: widget.initialValue); super.initState(); } @override void didUpdateWidget(covariant OracleUserAutoCompleteField oldWidget) { - if (widget.initialValue != oldWidget.initialValue) { - _controller = TextEditingController(text: widget.initialValue); - } + // if (widget.initialValue != oldWidget.initialValue) { + // _controller = TextEditingController(text: widget.initialValue); + // } super.didUpdateWidget(oldWidget); } @override void dispose() { - _controller.dispose(); + // _controller.dispose(); super.dispose(); } @@ -59,16 +60,18 @@ class _AutoCompletePartsFieldState extends State { // boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)], ), child: Autocomplete( + key: ValueKey(widget.initialValue), optionsBuilder: (TextEditingValue textEditingValue) async { if (textEditingValue.text.isEmpty) { return const Iterable.empty(); } return await assetDeliveryProvider!.getOracleUserAutoCompleteDetails(query: textEditingValue.text); }, + initialValue: TextEditingValue(text: widget.initialValue), displayStringForOption: (OracleUserModel option) => option.fullName ?? "", fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) { return TextField( - controller: _controller, + controller: fieldTextEditingController, focusNode: fieldFocusNode, style: AppTextStyles.bodyText.copyWith(color: AppColor.black10), textAlign: TextAlign.start, @@ -101,11 +104,11 @@ class _AutoCompletePartsFieldState extends State { ); }, onSelected: (OracleUserModel selection) { - if (widget.clearAfterPick) { - _controller.clear(); - } else { - _controller.text = selection.fullName ?? ''; - } + // if (widget.clearAfterPick) { + // fieldTextEditingController.clear(); + // } else { + // _controller.text = selection.fullName ?? ''; + // } widget.onPick(selection); }, ), diff --git a/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart b/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart index 2918d5b2..ecccc451 100644 --- a/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart @@ -431,12 +431,12 @@ class _UpdateEndUserAssetDetailsViewState extends State { backgroundColor: AppColor.fieldBgColor(context), controller: serialNoCtrl, initialValue: model.serialNo ?? '', + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, textAlign: TextAlign.center, labelStyle: AppTextStyles.textFieldLabelStyle, textInputType: TextInputType.number, @@ -379,7 +383,13 @@ class _UpdateChildAssetViewState extends State { ///Need to check which details show here... void _saveTap() async { + if(!_formKey.currentState!.validate()) return; + if(assetDeliveryProvider.childFormModel.installationDate==null){ + "Installation Date is Required".showToast; + return; + } _formKey.currentState!.save(); + //Need to check if not auto update. // assetDeliveryProvider.parentFormModel.childAssetList?[widget.index]= assetDeliveryProvider.childFormModel; Navigator.pop(context); diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart index 024dfe5f..14944824 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart @@ -10,7 +10,7 @@ import 'package:test_sa/models/new_models/building.dart'; import 'package:test_sa/models/new_models/floor.dart'; import 'package:test_sa/models/new_models/site.dart'; import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_asset_model.dart'; -import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/asset_delivery_utils.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_utils.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/search_asset_view.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/status_checklist_view.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_asset_card_view.dart'; @@ -270,6 +270,10 @@ class _UpdateParentAssetViewState extends State { backgroundColor: AppColor.fieldBgColor(context), controller: serialNoCtrl, initialValue: model.serialNo ?? '', + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, textAlign: TextAlign.center, labelStyle: AppTextStyles.textFieldLabelStyle, textInputType: TextInputType.number, @@ -424,6 +428,11 @@ class _UpdateParentAssetViewState extends State { ///Need to check which details show here... void _saveTap() async { + if (!_formKey.currentState!.validate()) return; + if (assetDeliveryProvider.parentFormModel.installationDate == null) { + "Installation Date is Required".showToast; + return; + } _formKey.currentState!.save(); assetDeliveryProvider.parentFormModel.lineId = widget.lineId; diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/update_technical_inspection_lines_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/update_technical_inspection_lines_view.dart index f64f1461..035fffe3 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/update_technical_inspection_lines_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/update_technical_inspection_lines_view.dart @@ -179,6 +179,10 @@ class _UpdateTechnicalInspectionLinesViewState extends State(context, listen: false).user?.type) == UsersTypes.engineer; bool isUserFMS = Provider.of(context, listen: false).isUserFMS; + bool commissioningEng = context.userProvider.isCommissioningEngineer; List navItems = [ NavItemModel(0, "overview", context.translation.overview), NavItemModel(1, "request_icon", context.translation.workOrder), - //TODO unCommit this to disable task - // if (!isEngineer||isUserFMS) - NavItemModel(2, "add_icon", context.translation.calendar, showLabel: false), + if (!commissioningEng) NavItemModel(2, "add_icon", context.translation.calendar, showLabel: false), NavItemModel(3, "assets", context.translation.assets), NavItemModel(4, "contact", context.translation.contact), ]; From 520e7af640694ab5e59c156ac04a4b9f0a326039 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 18 Jan 2026 15:05:47 +0300 Subject: [PATCH 33/35] fcm initialization changed to landing page --- lib/dashboard_latest/dashboard_view.dart | 16 ++++++++-------- lib/new_views/pages/land_page/land_page.dart | 12 +++++++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/dashboard_latest/dashboard_view.dart b/lib/dashboard_latest/dashboard_view.dart index 987e76cd..bf9b5a71 100644 --- a/lib/dashboard_latest/dashboard_view.dart +++ b/lib/dashboard_latest/dashboard_view.dart @@ -73,14 +73,14 @@ class _DashboardViewState extends State { // await requestDetailProvider.engineerRejectWorkOrder(id: '3', feedBack: 'Abcdef'); // await dashBoardProvider.getDashBoardCount(usersType: user.type!); // await getAllRequests(); - if (isFCM) { - FirebaseNotificationManger.initialized(context); - NotificationManger.initialisation((notificationDetails) { - FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!)); - }, (id, title, body, payload) async {}); - - isFCM = false; - } + // if (isFCM) { + // FirebaseNotificationManger.initialized(context); + // NotificationManger.initialisation((notificationDetails) { + // FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!)); + // }, (id, title, body, payload) async {}); + // + // isFCM = false; + // } }); } diff --git a/lib/new_views/pages/land_page/land_page.dart b/lib/new_views/pages/land_page/land_page.dart index b28b6eb2..ea07c085 100644 --- a/lib/new_views/pages/land_page/land_page.dart +++ b/lib/new_views/pages/land_page/land_page.dart @@ -1,8 +1,11 @@ +import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:test_sa/controllers/notification/firebase_notification_manger.dart'; +import 'package:test_sa/controllers/notification/notification_manger.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/controllers/providers/settings/app_settings.dart'; import 'package:test_sa/dashboard_latest/dashboard_view.dart'; @@ -42,7 +45,7 @@ class _LandPageState extends State { bool showAppbar = true; late List _pages; UserProvider? _userProvider; - + bool isFCM = true; @override void initState() { _pages = []; @@ -123,7 +126,14 @@ class _LandPageState extends State { // if (_userProvider!.user!.type != UsersTypes.engineer) const CalendarPage(), const MyAssetsPage(fromBottomBar: true), ]; + if (isFCM) { + FirebaseNotificationManger.initialized(context); + NotificationManger.initialisation((notificationDetails) { + FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!)); + }, (id, title, body, payload) async {}); + isFCM = false; + } checkLocalAuth(); } From a0a084803d658c0c48192f7a5dc2ce3e44b30bb1 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Tue, 20 Jan 2026 10:01:46 +0300 Subject: [PATCH 34/35] UI improvements --- lib/extensions/enum_extensions.dart | 24 + lib/models/lookup.dart | 6 +- .../helper_function.dart | 3 +- .../models/asset_delivery_data_model.dart | 1 + .../pages/asset_delivery_page.dart | 48 +- .../pages/asset_delivery_stage_tab_page.dart | 90 ++++ .../pages/asset_delivery_table_card_view.dart | 47 +- .../pages/attachment_view.dart | 6 +- .../delivery_inspection_form_view.dart | 65 ++- .../end_user_cost_center_list_view.dart | 20 +- .../update_end_user_asset_details_view.dart | 1 - .../pages/history_log_view.dart | 12 +- .../status_checklist_view.dart | 216 ++++++--- .../technical_inspection_lines_list_view.dart | 43 +- .../update_child_asset_view.dart | 9 +- .../update_parent_asset_view.dart | 454 +----------------- 16 files changed, 444 insertions(+), 601 deletions(-) create mode 100644 lib/modules/asset_delivery_module/pages/asset_delivery_stage_tab_page.dart diff --git a/lib/extensions/enum_extensions.dart b/lib/extensions/enum_extensions.dart index 991a0f81..24d0a479 100644 --- a/lib/extensions/enum_extensions.dart +++ b/lib/extensions/enum_extensions.dart @@ -121,6 +121,24 @@ extension StringExtensionsRecurrentTaskInpesctionDataType on String { } } } + + +extension IntExtensionsAssetDeliveryStage on int { + AssetDeliveryStageEnum toAssetDeliveryStageEnum() { + switch (this) { + case 2: + return AssetDeliveryStageEnum.deliveryInspection; + case 3: + return AssetDeliveryStageEnum.technicalInspection; + case 4: + return AssetDeliveryStageEnum.endUserAcceptance; + default: + return AssetDeliveryStageEnum.other; + } + } +} + + enum DropDownMatchType { identifier, name, @@ -131,3 +149,9 @@ enum CostCenterType { costCenter, serialNumber, } +enum AssetDeliveryStageEnum { + deliveryInspection, + technicalInspection, + endUserAcceptance, + other, +} diff --git a/lib/models/lookup.dart b/lib/models/lookup.dart index 844e46df..1af16028 100644 --- a/lib/models/lookup.dart +++ b/lib/models/lookup.dart @@ -7,9 +7,10 @@ class Lookup extends Base { int? id; // Now nullable int? value; // Now nullable WorkOrderNextStepEnum? workOrderNextStepEnum; // Now nullable + AssetDeliveryStageEnum? assetDeliveryStageEnum; // Now nullable String? name; // Now nullable - Lookup({this.id, this.value, this.name, this.workOrderNextStepEnum}) : super(identifier: id?.toString(), name: name); + Lookup({this.id, this.value, this.name, this.workOrderNextStepEnum, this.assetDeliveryStageEnum}) : super(identifier: id?.toString(), name: name); @override bool operator ==(Object other) => identical(this, other) || other is Lookup && ((value != null && value == other.value) || (id != null && id == other.id)); @@ -38,10 +39,11 @@ class Lookup extends Base { // } factory Lookup.fromJson(Map? parsedJson) { - if(parsedJson==null) return Lookup(); + if (parsedJson == null) return Lookup(); return Lookup( name: parsedJson["name"], workOrderNextStepEnum: parsedJson["value"] == null ? null : (parsedJson["value"] as int).toWorkOrderNextStepEnum(), + assetDeliveryStageEnum: parsedJson["value"] == null ? null : (parsedJson["value"] as int).toAssetDeliveryStageEnum(), id: parsedJson["id"], value: parsedJson["value"], ); diff --git a/lib/modules/asset_delivery_module/helper_function.dart b/lib/modules/asset_delivery_module/helper_function.dart index b34a749c..0cfb122e 100644 --- a/lib/modules/asset_delivery_module/helper_function.dart +++ b/lib/modules/asset_delivery_module/helper_function.dart @@ -10,7 +10,7 @@ import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; class HelperFunction { - static void attachmentTap({required BuildContext context, required AssetDeliveryProvider assetDeliveryProvider, required dynamic deliveryTableItemId}) async { + static void attachmentTap({required BuildContext context, bool viewOnly = false, required AssetDeliveryProvider assetDeliveryProvider, required dynamic deliveryTableItemId}) async { Lookup? result; result = await showAttachmentTypeBottomSheet(context); List list = []; @@ -30,6 +30,7 @@ class HelperFunction { builder: (context) => AssetDeliveryAttachmentView( tableItemId: deliveryTableItemId, attachmentType: result!.value, + viewOnly: viewOnly, attachmentList: list, ))); // } diff --git a/lib/modules/asset_delivery_module/models/asset_delivery_data_model.dart b/lib/modules/asset_delivery_module/models/asset_delivery_data_model.dart index cde76346..15ece8ca 100644 --- a/lib/modules/asset_delivery_module/models/asset_delivery_data_model.dart +++ b/lib/modules/asset_delivery_module/models/asset_delivery_data_model.dart @@ -305,6 +305,7 @@ class AssetDeliveryTableModel { status = json['status'] != null ? Lookup.fromJson(json['status']) : null; + } Map toJson() { diff --git a/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart b/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart index b3a3174b..a4ce0983 100644 --- a/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart +++ b/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart @@ -6,6 +6,7 @@ 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/modules/asset_delivery_module/models/asset_delivery_data_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_stage_tab_page.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_table_card_view.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/change_status_bottomsheet.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart'; @@ -35,6 +36,7 @@ class AssetDeliveryPage extends StatefulWidget { class _AssetDeliveryPageState extends State { AssetDeliveryDataModel? dataModel; AssetDeliveryProvider? assetDeliveryProvider; + bool loading = false; @override void initState() { @@ -46,12 +48,18 @@ class _AssetDeliveryPageState extends State { } Future getDetailsById() async { + setState(() { + loading = true; + }); final futures = [ assetDeliveryProvider!.getAssetDeliveryDetailById(requestId: widget.requestId).then((value) => dataModel = value), assetDeliveryProvider!.getAssetDeliveryTableListById(requestId: widget.requestId), ]; await Future.wait(futures); + setState(() { + loading = false; + }); } @override @@ -68,7 +76,7 @@ class _AssetDeliveryPageState extends State { body: Consumer( builder: (context, provider, child) { final dataModel = provider.assetDeliveryDataModel; - if (provider.isLoading) { + if (loading) { return const CircularProgressIndicator(color: AppColor.primary10).center; } if (dataModel == null) { @@ -246,16 +254,17 @@ class _AssetDeliveryPageState extends State { style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), ), 8.height, - Text( - 'EDD (Estimated Delivery Date)'.addTranslation, - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), - ), - 8.height, - Text( - dataModel?.edd != null ? dataModel!.edd!.toAssetDetailsFormat : '-', - style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), - ), - 8.height, + //Removed as request by Ahmed backend... + // Text( + // 'EDD (Estimated Delivery Date)'.addTranslation, + // style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + // ), + // 8.height, + // Text( + // dataModel?.edd != null ? dataModel!.edd!.toAssetDetailsFormat : '-', + // style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + // ), + // 8.height, Text( 'Total'.addTranslation, style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), @@ -321,10 +330,21 @@ class _AssetDeliveryPageState extends State { }); }); } else { + // Navigator.of(context).push(MaterialPageRoute( + // builder: (_) => + // DeliveryInspectionFormView( + // deliveryTableModel: model, + // requestModel: dataModel, + // ), + // )); Navigator.of(context).push(MaterialPageRoute( - builder: (_) => DeliveryInspectionFormView( - deliveryTableModel: model, - requestModel: dataModel, + builder: (_) => AssetDeliveryStageTabPage( + title: 'Delivery Inspection'.addTranslation, + detailWidget: DeliveryInspectionFormView( + deliveryTableModel: model, + requestModel: dataModel, + ), + tableId: model.id, ), )); } diff --git a/lib/modules/asset_delivery_module/pages/asset_delivery_stage_tab_page.dart b/lib/modules/asset_delivery_module/pages/asset_delivery_stage_tab_page.dart new file mode 100644 index 00000000..959beffc --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/asset_delivery_stage_tab_page.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/enum_extensions.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/modules/asset_delivery_module/pages/history_log_view.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; + +class AssetDeliveryStageTabPage extends StatefulWidget { + String title; + Widget detailWidget; + int? tableId; + + AssetDeliveryStageTabPage({ + Key? key, + required this.title, + required this.detailWidget, + this.tableId, + }) : super(key: key); + + @override + _AssetDeliveryStageTabPageState createState() { + return _AssetDeliveryStageTabPageState(); + } +} + +class _AssetDeliveryStageTabPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + appBar: DefaultAppBar( + title: widget.title, + ), + body: DefaultTabController( + length: 2, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: EdgeInsets.only(left: 16.toScreenWidth, right: 16.toScreenWidth, top: 12.toScreenHeight), + decoration: BoxDecoration(color: context.isDark ? AppColor.neutral50 : AppColor.white10, borderRadius: BorderRadius.circular(10)), + child: TabBar( + padding: EdgeInsets.symmetric(vertical: 4.toScreenHeight, horizontal: 4.toScreenWidth), + labelColor: context.isDark ? AppColor.neutral30 : AppColor.black20, + unselectedLabelColor: context.isDark ? AppColor.neutral30 : AppColor.black20, + unselectedLabelStyle: AppTextStyles.bodyText, + labelStyle: AppTextStyles.bodyText, + indicatorPadding: EdgeInsets.zero, + indicatorSize: TabBarIndicatorSize.tab, + dividerColor: Colors.transparent, + indicator: BoxDecoration(color: context.isDark ? AppColor.neutral60 : AppColor.neutral110, borderRadius: BorderRadius.circular(7)), + tabs: [ + Tab(text: context.translation.details, height: 57.toScreenHeight), + Tab(text: context.translation.historyLogs, height: 57.toScreenHeight), + ], + ), + ), + // 12.height, + TabBarView( + children: [ + widget.detailWidget, + AssetDeliveryHistoryLogView( + tableItemId: widget.tableId, + ), + ], + ).expanded, + ], + ), + )); + } + + + String getPageTitle({AssetDeliveryStageEnum? deliveryStage}) { + if (deliveryStage == AssetDeliveryStageEnum.deliveryInspection) { + return 'Delivery Inspection'.addTranslation; + } + if (deliveryStage == AssetDeliveryStageEnum.technicalInspection) { + return 'Technical Inspection'.addTranslation; + } + if (deliveryStage == AssetDeliveryStageEnum.endUserAcceptance) { + return 'End-User Acceptance'.addTranslation; + } else { + return 'Details'.addTranslation; + } + } +} diff --git a/lib/modules/asset_delivery_module/pages/asset_delivery_table_card_view.dart b/lib/modules/asset_delivery_module/pages/asset_delivery_table_card_view.dart index 0d906d49..e7a41d96 100644 --- a/lib/modules/asset_delivery_module/pages/asset_delivery_table_card_view.dart +++ b/lib/modules/asset_delivery_module/pages/asset_delivery_table_card_view.dart @@ -26,28 +26,33 @@ class AssetDeliveryTableCard extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - StatusLabel( - label: deliveryTableModel.status?.name, - textColor: AppColor.neutral50, - backgroundColor: AppColor.orange30, - ), - Row( - children: [ - 'history_icon'.toSvgAsset(height: 14, width: 14), - 8.width, - Text( - 'View History'.addTranslation, - style: AppTextStyles.bodyText.copyWith(color: AppColor.primary10), - ), - ], - ).onPress(() { - onViewHistoryTap(); - }), - ], + StatusLabel( + label: deliveryTableModel.status?.name, + textColor: AppColor.neutral50, + backgroundColor: AppColor.orange30, ), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // StatusLabel( + // label: deliveryTableModel.status?.name, + // textColor: AppColor.neutral50, + // backgroundColor: AppColor.orange30, + // ), + // Row( + // children: [ + // 'history_icon'.toSvgAsset(height: 14, width: 14), + // 8.width, + // Text( + // 'View History'.addTranslation, + // style: AppTextStyles.bodyText.copyWith(color: AppColor.primary10), + // ), + // ], + // ).onPress(() { + // onViewHistoryTap(); + // }), + // ], + // ), 8.height, Text( 'Delivery Number: ${deliveryTableModel.number ?? '-'}', diff --git a/lib/modules/asset_delivery_module/pages/attachment_view.dart b/lib/modules/asset_delivery_module/pages/attachment_view.dart index e98ab0af..2f4020e7 100644 --- a/lib/modules/asset_delivery_module/pages/attachment_view.dart +++ b/lib/modules/asset_delivery_module/pages/attachment_view.dart @@ -24,11 +24,13 @@ import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; class AssetDeliveryAttachmentView extends StatefulWidget { final int? tableItemId; final int? attachmentType; + bool viewOnly = false; final List attachmentList; - const AssetDeliveryAttachmentView({ + AssetDeliveryAttachmentView({ super.key, required this.attachmentList, + this.viewOnly=false, this.attachmentType, this.tableItemId, }); @@ -80,7 +82,7 @@ class _AssetDeliveryAttachmentViewState extends State Widget build(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, - appBar: DefaultAppBar( - title: 'Delivery Inspection'.addTranslation, - onBackPress: () { - Navigator.pop(context); - }, - ), + // appBar: DefaultAppBar( + // title: 'Delivery Inspection'.addTranslation, + // onBackPress: () { + // Navigator.pop(context); + // }, + // ), body: isLoading ? const CircularProgressIndicator(color: AppColor.primary10).center : Form( @@ -164,7 +167,20 @@ class _DeliveryInspectionFormViewState extends State ), ], ) - : AppFilledButton(buttonColor: AppColor.primary10, label: 'Next'.addTranslation, maxWidth: true, onPressed: _nextTap), + : Row( + children: [ + AppFilledButton( + buttonColor: AppColor.white60, + label: 'Attachments'.addTranslation, + maxWidth: true, + textColor: AppColor.black10, + onPressed: () { + HelperFunction.attachmentTap(context: context, viewOnly: true, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id); + }).expanded, + 8.width, + AppFilledButton(buttonColor: AppColor.primary10, label: 'Next'.addTranslation, maxWidth: true, onPressed: _nextTap).expanded, + ], + ), ), ], ), @@ -618,21 +634,44 @@ class _DeliveryInspectionFormViewState extends State void _nextTap() { if (widget.deliveryTableModel.isContainsTechnicalInspection == true) { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => TechnicalInspectionLinesListView( + // deliveryTableModel: widget.deliveryTableModel, + // requestModel: widget.requestModel, + // )) + // ); Navigator.push( context, MaterialPageRoute( - builder: (context) => TechnicalInspectionLinesListView( - deliveryTableModel: widget.deliveryTableModel, - requestModel: widget.requestModel, + builder: (context) => AssetDeliveryStageTabPage( + title: 'Technical Inspection'.addTranslation, + detailWidget: TechnicalInspectionLinesListView( + deliveryTableModel: widget.deliveryTableModel, + requestModel: widget.requestModel, + ), + tableId: widget.deliveryTableModel.id, ))); } else { Navigator.push( context, MaterialPageRoute( - builder: (context) => EndUserCostCenterLIstView( - deliveryTableModel: widget.deliveryTableModel, - requestModel: widget.requestModel, + builder: (context) => AssetDeliveryStageTabPage( + title: 'End-User Acceptance'.addTranslation, + detailWidget: EndUserCostCenterLIstView( + deliveryTableModel: widget.deliveryTableModel, + requestModel: widget.requestModel, + ), + tableId: widget.deliveryTableModel.id, ))); + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => EndUserCostCenterLIstView( + // deliveryTableModel: widget.deliveryTableModel, + // requestModel: widget.requestModel, + // ))); } } diff --git a/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart b/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart index 92979620..f032d75a 100644 --- a/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart @@ -72,10 +72,10 @@ class _EndUserCostCenterLIstViewState extends State { Widget build(BuildContext context) { return Scaffold( key: _scaffoldKey, - appBar: DefaultAppBar( - title: 'End-User Acceptance'.addTranslation, - titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), - ), + // appBar: DefaultAppBar( + // title: 'End-User Acceptance'.addTranslation, + // titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + // ), body: Form( key: _formKey, child: isLoading @@ -108,7 +108,17 @@ class _EndUserCostCenterLIstViewState extends State { ), ], )) - : const SizedBox(), + : FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + buttonColor: AppColor.white60, + label: 'Attachments'.addTranslation, + maxWidth: true, + textColor: AppColor.black10, + onPressed: () { + HelperFunction.attachmentTap(context: context, viewOnly: true, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id); + }), + ), ], ), ), diff --git a/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart b/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart index ecccc451..834b96b3 100644 --- a/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart @@ -292,7 +292,6 @@ class _UpdateEndUserAssetDetailsViewState extends State(builder: (context, provider, _) { return provider.isLoading ? const CircularProgressIndicator(color: AppColor.primary10).center diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/status_checklist_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/status_checklist_view.dart index 9a574d69..e98c1213 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/status_checklist_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/status_checklist_view.dart @@ -13,34 +13,27 @@ 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'; -class InspectionChecklistBottomSheet extends StatefulWidget { +class InspectionCheckListView extends StatefulWidget { final List? initialValues; - const InspectionChecklistBottomSheet({super.key, this.initialValues}); + const InspectionCheckListView({super.key, this.initialValues}); @override - State createState() => - _InspectionChecklistBottomSheetState(); + State createState() => _InspectionCheckListViewState(); } -class _InspectionChecklistBottomSheetState - extends State { +class _InspectionCheckListViewState extends State { late List checklist; @override void initState() { super.initState(); + checklist = AssetDeliveryUtils.inspectionChecklist.map((e) => InspectionChecklistItem(title: e.title)).toList(); - /// Create local copy (no shared mutation) - checklist = AssetDeliveryUtils.inspectionChecklist - .map((e) => InspectionChecklistItem(title: e.title)) - .toList(); - - /// Apply initial values only if provided if (widget.initialValues != null && widget.initialValues!.isNotEmpty) { for (final item in checklist) { final matched = widget.initialValues!.firstWhere( - (e) => e.title == item.title, + (e) => e.title == item.title, orElse: () => InspectionChecklistItem(), ); item.status = matched.status; // null-safe @@ -54,70 +47,36 @@ class _InspectionChecklistBottomSheetState }); } - List get selectedValues => - checklist.where((e) => e.status != null).toList(); + List get selectedValues => checklist.where((e) => e.status != null).toList(); @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: AppColor.white10, appBar: DefaultAppBar( title: 'Asset Check List'.addTranslation, - showBackButton: false, - titleStyle: AppTextStyles.heading3.copyWith( - fontWeight: FontWeight.w500, - color: context.isDark - ? AppColor.neutral30 - : AppColor.neutral50, - ), ), body: Column( children: [ - 12.height, - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: checklist.length, - padding: const EdgeInsets.symmetric(horizontal: 12), - itemBuilder: (context, index) { - final item = checklist[index]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - item.title ?? '', - style: AppTextStyles.heading6.copyWith( - color: context.isDark - ? AppColor.neutral30 - : AppColor.black10, - ), - ), - Row( - children: [ - Expanded( - child: RadioListTile( - title: const Text('Pass'), - value: 'PASS', - groupValue: item.status, - onChanged: (v) => - updateStatus(index, v!), - ), - ), - Expanded( - child: RadioListTile( - title: const Text('Fail'), - value: 'FAIL', - groupValue: item.status, - onChanged: (v) => - updateStatus(index, v!), - ), - ), - ], - ), - ], - ); - }, - ).expanded, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: List.generate( + checklist.length, + (index) { + final model = checklist[index]; + return inspectionStatusRadioWidget( + model: model, + index: index, + context: context, + ); + }, + ), + ).toShadowContainer( + context, + borderRadius: 20, + margin: const EdgeInsets.only(top: 12, left: 12, right: 12), + ), + const Spacer(), FooterActionButton.footerContainer( context: context, child: AppFilledButton( @@ -133,5 +92,124 @@ class _InspectionChecklistBottomSheetState ), ); } -} + Widget inspectionStatusRadioWidget({required int index, required InspectionChecklistItem model, required BuildContext context}) { + bool status = (model.status == null || model.status == null) ? true : (model.status != null ? (model.status == 'true' ? true : false) : false); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + model.title ?? '', + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? Colors.white : AppColor.white936), + ), + 14.height, + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // + // (status ? 'Pass' : 'Fail').bodyText2(context).custom(color: AppColor.neutral120, fontWeight: FontWeight.w500), + // ], + // ).expanded, + GestureDetector( + onTap: () { + setState(() { + status = !status; + model.status = status.toString(); + }); + }, + child: Container( + width: 99.toScreenWidth, + height: 40.toScreenHeight, + padding: EdgeInsetsDirectional.all(4.toScreenHeight), + decoration: BoxDecoration( + color: AppColor.fieldBgColor(context), + borderRadius: BorderRadius.circular(5), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + buildToggleOption( + label: "PASS", + isActive: status, + activeColor: AppColor.green20, + inactiveColor: Colors.transparent, + textColor: status + ? AppColor.green50 + : context.isDark + ? Colors.white + : AppColor.black20, + ), + buildToggleOption( + label: "FAIL", + isActive: !status, + activeColor: AppColor.red20, + inactiveColor: Colors.transparent, + textColor: status + ? context.isDark + ? Colors.white + : AppColor.black20 + : AppColor.red30, + ), + ], + ), + ), + ), + ], + ).paddingOnly(top: 4, bottom: 4); + } + + Widget buildToggleOption({ + required String label, + required bool isActive, + required Color activeColor, + required Color inactiveColor, + required Color textColor, + }) { + return Container( + width: 44.toScreenWidth, + height: 30.toScreenHeight, + alignment: Alignment.center, + decoration: BoxDecoration( + color: isActive ? activeColor : inactiveColor, + borderRadius: BorderRadius.circular(3), + ), + child: label.bodyText2(context).custom(color: textColor), + ); + } + +//older widget ... +// return Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text( +// item.title ?? '', +// style: AppTextStyles.heading6.copyWith( +// color: context.isDark ? AppColor.neutral30 : AppColor.black10, +// ), +// ), +// Row( +// children: [ +// Expanded( +// child: RadioListTile( +// title: const Text('Pass'), +// value: 'PASS', +// groupValue: item.status, +// onChanged: (v) => updateStatus(index, v!), +// ), +// ), +// Expanded( +// child: RadioListTile( +// title: const Text('Fail'), +// value: 'FAIL', +// groupValue: item.status, +// onChanged: (v) => updateStatus(index, v!), +// ), +// ), +// ], +// ), +// ], +// ); +} diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart index b4c6d505..e2c05093 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart @@ -12,6 +12,7 @@ import 'package:test_sa/modules/asset_delivery_module/helper_function.dart'; import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart'; import 'package:test_sa/modules/asset_delivery_module/models/delivery_line_model.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_line_card_view.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_stage_tab_page.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/attachment_view.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart'; import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/update_technical_inspection_lines_view.dart'; @@ -79,10 +80,10 @@ class _TechnicalInspectionLinesListViewState extends State EndUserCostCenterLIstView( + // requestModel: widget.requestModel, + // deliveryTableModel: widget.deliveryTableModel, + // ))); + Navigator.push( context, MaterialPageRoute( - builder: (context) => EndUserCostCenterLIstView( - requestModel: widget.requestModel, - deliveryTableModel: widget.deliveryTableModel, + builder: (context) => AssetDeliveryStageTabPage( + title: 'End-User Acceptance'.addTranslation, + detailWidget: EndUserCostCenterLIstView( + deliveryTableModel: widget.deliveryTableModel, + requestModel: widget.requestModel, + ), + tableId: widget.deliveryTableModel.id, ))); } } diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart index 07c7450d..3e151cb9 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart @@ -25,8 +25,6 @@ import '../../../../models/new_models/department.dart'; import '../../../../new_views/common_widgets/app_filled_button.dart'; import '../../../../new_views/common_widgets/default_app_bar.dart'; -//TODO need to refactor this screen when done ... - class UpdateChildAssetView extends StatefulWidget { final TechnicalInspectionAssetModel assetModel; final int? lineId; @@ -183,7 +181,6 @@ class _UpdateChildAssetViewState extends State { await provider.getSiteData(siteId: assetModel.site?.id, model: provider.childFormModel); } provider.setParentModel(assetModel); - log('child list length ${assetModel.childAssetList?.length}'); populateTextFields(model: provider.childFormModel); } } @@ -383,8 +380,8 @@ class _UpdateChildAssetViewState extends State { ///Need to check which details show here... void _saveTap() async { - if(!_formKey.currentState!.validate()) return; - if(assetDeliveryProvider.childFormModel.installationDate==null){ + if (!_formKey.currentState!.validate()) return; + if (assetDeliveryProvider.childFormModel.installationDate == null) { "Installation Date is Required".showToast; return; } @@ -401,7 +398,7 @@ class _UpdateChildAssetViewState extends State { final result = await Navigator.push( context, MaterialPageRoute( - builder: (context) => InspectionChecklistBottomSheet( + builder: (context) => InspectionCheckListView( initialValues: checkList, ))); if (result != null) { diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart index 14944824..ceafcfe6 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart @@ -28,8 +28,6 @@ import '../../../../models/new_models/department.dart'; import '../../../../new_views/common_widgets/app_filled_button.dart'; import '../../../../new_views/common_widgets/default_app_bar.dart'; -//TODO need to refactor this screen also add loaders when done ... - class UpdateParentAssetView extends StatefulWidget { final TechnicalInspectionAssetModel assetModel; final int? lineId; @@ -438,7 +436,6 @@ class _UpdateParentAssetViewState extends State { assetDeliveryProvider.parentFormModel.tableItemId = widget.tableItemId; assetDeliveryProvider.parentFormModel.costCenterId = widget.costCenterItemId; - log('model data ${assetDeliveryProvider.parentFormModel.toJson()}'); showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); await assetDeliveryProvider.saveTechnicalInspectionAssetData(model: assetDeliveryProvider.parentFormModel).then((status) async { Navigator.pop(context); @@ -453,7 +450,7 @@ class _UpdateParentAssetViewState extends State { final result = await Navigator.push( context, MaterialPageRoute( - builder: (context) => InspectionChecklistBottomSheet( + builder: (context) => InspectionCheckListView( initialValues: checkList, ))); @@ -462,18 +459,6 @@ class _UpdateParentAssetViewState extends State { setState(() {}); } buildInspectionPayload(checkList); - if (checkList.isNotEmpty) { - final isPass = checkList.every((e) => e.status == 'PASS'); - log('Final Status: ${isPass ? 'PASS' : 'FAIL'}'); - } - -// call this as bottomsheet. - // final result = await showModalBottomSheet>>( - // context: context, - // isScrollControlled: true, - // backgroundColor: Colors.transparent, - // builder: (_) => const InspectionChecklistBottomSheet(), - // ); } bool _isPass(List list, String title) { @@ -528,446 +513,9 @@ class _UpdateParentAssetViewState extends State { break; } } - checkList.forEach((item) { - log('items ${item.toJson()}'); - }); } String _statusFromBool(bool? value) { return value == true ? 'PASS' : 'FAIL'; } } - -// import 'dart:developer'; -// import 'package:flutter/material.dart'; -// import 'package:provider/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/new_models/building.dart'; -// import 'package:test_sa/models/new_models/floor.dart'; -// import 'package:test_sa/models/new_models/site.dart'; -// import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_asset_model.dart'; -// import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/search_asset_view.dart'; -// import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/status_checklist_view.dart'; -// import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_asset_card_view.dart'; -// import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart'; -// import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart'; -// import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; -// import 'package:test_sa/new_views/app_style/app_color.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/gas_request_providers/site_provider.dart'; -// import 'package:test_sa/providers/loading_list_notifier.dart'; -// import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; -// import '../../../../models/new_models/department.dart'; -// import '../../../../new_views/common_widgets/app_filled_button.dart'; -// import '../../../../new_views/common_widgets/default_app_bar.dart'; -// -// //TODO need to refactor this screen when done ... -// -// class UpdateParentAssetView extends StatefulWidget { -// final TechnicalInspectionParentAssetModel assetModel; -// final int? lineId; -// -// UpdateParentAssetView({ -// Key? key, -// required this.assetModel, -// this.lineId, -// }) : super(key: key); -// -// @override -// State createState() => _UpdateParentAssetViewState(); -// } -// -// class _UpdateParentAssetViewState extends State { -// final GlobalKey _formKey = GlobalKey(); -// final GlobalKey _scaffoldKey = GlobalKey(); -// List checkList = []; -// late SiteProvider siteProvider; -// late AssetDeliveryProvider assetDeliveryProvider; -// -// @override -// void initState() { -// siteProvider = Provider.of(context, listen: false); -// assetDeliveryProvider = Provider.of(context, listen: false); -// // WidgetsBinding.instance.addPostFrameCallback((_) { -// populateForm(); -// // }); -// super.initState(); -// } -// -// void populateForm() async { -// // _model = widget.assetModel; -// assetDeliveryProvider.setModel(widget.assetModel); -// applyInspectionFlagsToChecklist(model: widget.assetModel,checklist: checkList); -// if (widget.assetModel.site != null) { -// await assetDeliveryProvider.getSiteData(siteId: widget.assetModel.site?.id, model: _model); -// } -// setState(() {}); -// } -// -// @override -// void dispose() { -// super.dispose(); -// } -// -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// key: _scaffoldKey, -// appBar: DefaultAppBar( -// title: 'Parent Details'.addTranslation, -// titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), -// ), -// body: Form( -// key: _formKey, -// child: Column( -// children: [ -// SingleChildScrollView( -// padding: const EdgeInsets.all(16), -// child: Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// inspectionDetailsForm(lineId: widget.lineId), -// 8.height, -// const Divider().defaultStyle(context), -// 8.height, -// childList(context), -// ], -// ).toShadowContainer(context, borderRadius: 20, padding: 12)) -// .expanded, -// FooterActionButton.footerContainer(context: context, child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Save'.addTranslation, maxWidth: true, onPressed: _saveTap)), -// ], -// ), -// ), -// ); -// } -// -// Widget childList(BuildContext context) { -// final assetDeliveryTableList = ['abc', 'def', 'ghi', 'fhg', 'hjhh']; -// return Column( -// mainAxisSize: MainAxisSize.min, -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Row( -// children: [ -// Text( -// "Test List", -// style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), -// ), -// 8.width, -// 'web_link_icon'.toSvgAsset().onPress(() { -// openInspectionChecklist(context); -// }), -// ], -// ), -// 8.height, -// ListView.separated( -// itemCount: assetDeliveryTableList.length, -// separatorBuilder: (_, __) => 8.height, -// padding: EdgeInsets.zero, -// shrinkWrap: true, -// physics: const NeverScrollableScrollPhysics(), -// itemBuilder: (listContext, itemIndex) { -// return TechnicalInspectionAssetCard( -// assetModel: TechnicalInspectionParentAssetModel(), -// ischild: true, -// editPress: () { -// Navigator.push(context, MaterialPageRoute(builder: (context) => UpdateChildAssetView())); -// }, -// isViewOnly: false, -// ); -// }, -// ), -// ], -// ); -// } -// -// Widget inspectionDetailsForm({int? lineId}) { -// return Column( -// children: [ -// //Replace with simple container... -// AppTextFormField( -// labelText: 'Asset Number'.addTranslation, -// backgroundColor: AppColor.fieldBgColor(context), -// initialValue: _model.assetNumber, -// textAlign: TextAlign.center, -// labelStyle: AppTextStyles.textFieldLabelStyle, -// textInputType: TextInputType.number, -// showShadow: false, -// enable: false, -// style: Theme.of(context).textTheme.titleMedium, -// ).onPress(() async { -// var data = await Navigator.push( -// context, -// MaterialPageRoute( -// builder: (context) => TechnicalInspectionSearchAssetView( -// lineId: lineId, -// ))); -// if (data != null) { -// TechnicalInspectionParentAssetModel? assetModel = await assetDeliveryProvider.getAssetDetails(assetId: data?.id); -// -// if (assetModel != null) { -// _model =TechnicalInspectionParentAssetModel(); -// setState(() { -// -// }); -// setState(() { -// _model = assetModel; -// }); -// if (assetModel.site != null) { -// await assetDeliveryProvider.getSiteData(siteId: assetModel.site?.id, model: _model); -// } -// setState(() {}); -// } -// } -// }), -// 8.height, -// AppTextFormField( -// labelText: 'Serial Number'.addTranslation, -// backgroundColor: AppColor.fieldBgColor(context), -// initialValue: _model.serialNo ?? '', -// textAlign: TextAlign.center, -// labelStyle: AppTextStyles.textFieldLabelStyle, -// textInputType: TextInputType.number, -// showShadow: false, -// onSaved: (value) { -// _model.serialNo = value; -// }, -// style: Theme.of(context).textTheme.titleMedium, -// ), -// 8.height, -// AppTextFormField( -// labelText: 'System ID'.addTranslation, -// backgroundColor: AppColor.fieldBgColor(context), -// initialValue: _model.systemId ?? '', -// textAlign: TextAlign.center, -// labelStyle: AppTextStyles.textFieldLabelStyle, -// showShadow: false, -// onSaved: (value) { -// _model.systemId = value; -// }, -// style: Theme.of(context).textTheme.titleMedium, -// ), -// 8.height, -// SingleItemDropDownMenu( -// context: context, -// title: 'Site', -// initialValue: _model.site, -// loading: assetDeliveryProvider.isSiteLoading, -// showAsBottomSheet: true, -// backgroundColor: AppColor.fieldBgColor(context), -// showShadow: false, -// // enabled: false, -// onSelect: (value) { -// if (value == null) { -// return; -// } -// _model.site = value; -// _model.building = null; -// _model.floor = null; -// _model.department = null; -// setState(() {}); -// }, -// ), -// 8.height, -// SingleItemDropDownMenu( -// context: context, -// title: 'Building', -// backgroundColor: AppColor.fieldBgColor(context), -// showAsBottomSheet: true, -// loading: assetDeliveryProvider.isSiteLoading, -// showShadow: false, -// initialValue: _model.building, -// enabled: _model.site?.buildings?.isNotEmpty ?? false, -// staticData: _model.site?.buildings ?? [], -// onSelect: (value) { -// if (value == null) { -// return; -// } -// _model.building = value; -// _model.floor = null; -// _model.department = null; -// setState(() {}); -// }, -// ), -// 8.height, -// SingleItemDropDownMenu( -// context: context, -// showAsBottomSheet: true, -// backgroundColor: AppColor.fieldBgColor(context), -// loading: assetDeliveryProvider.isSiteLoading, -// showShadow: false, -// title: 'Floor', -// initialValue: _model.floor, -// enabled: _model.building?.floors?.isNotEmpty ?? false, -// staticData: _model.building?.floors ?? [], -// onSelect: (value) { -// if (value == null) { -// return; -// } -// _model.floor = value; -// _model.department = null; -// setState(() {}); -// }, -// ), -// 8.height, -// SingleItemDropDownMenu( -// context: context, -// title: 'Department', -// backgroundColor: AppColor.fieldBgColor(context), -// loading: assetDeliveryProvider.isSiteLoading, -// showAsBottomSheet: true, -// showShadow: false, -// initialValue: _model.department, -// enabled: _model.floor?.departments?.isNotEmpty ?? false, -// staticData: _model.floor?.departments ?? [], -// onSelect: (value) { -// if (value == null) { -// return; -// } -// _model.department = value; -// _model.room = null; -// setState(() {}); -// }, -// ), -// -// 8.height, -// ADatePicker( -// label: 'Installation Date'.addTranslation, -// hideShadow: true, -// height: 56.toScreenHeight, -// backgroundColor: AppColor.fieldBgColor(context), -// date: _model.installationDate, -// // from: widget.pickerFromDate, -// formatDateWithTime: true, -// onDatePicker: (selectedDate) { -// showTimePicker( -// context: context, -// initialTime: TimeOfDay.now(), -// builder: (BuildContext context, Widget? child) { -// final ThemeData currentTheme = Theme.of(context); -// return Theme( -// data: currentTheme.copyWith( -// timePickerTheme: TimePickerThemeData( -// dialHandColor: AppColor.primary10, -// dialBackgroundColor: Colors.grey.withOpacity(0.1), -// hourMinuteColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.grey.withOpacity(0.1)), -// dayPeriodColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.transparent), -// dayPeriodTextColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? Colors.white : AppColor.primary10), -// dayPeriodBorderSide: BorderSide(color: Colors.grey.withOpacity(0.2)), -// entryModeIconColor: AppColor.primary10, -// ), -// textButtonTheme: TextButtonThemeData(style: TextButton.styleFrom(foregroundColor: AppColor.primary10)), -// ), -// child: child!, -// ); -// }, -// ).then((selectedTime) { -// if (selectedTime != null) { -// _model.installationDate = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute); -// setState(() {}); -// } -// }); -// }, -// ), -// 8.height, -// ], -// ); -// } -// -// ///Need to check which details show here... -// -// void _saveTap() async { -// _formKey.currentState!.save(); -// } -// -// Future openInspectionChecklist(BuildContext context) async { -// final result = await Navigator.push( -// context, -// MaterialPageRoute( -// builder: (context) => InspectionChecklistBottomSheet( -// initialValues: checkList, -// ))); -// -// log('result i got is $result'); -// if(result!=null){ -// checkList = result; -// setState(() {}); -// } -// if (checkList.isNotEmpty) { -// final isPass = checkList.every((e) => e.status == 'PASS'); -// log('Final Status: ${isPass ? 'PASS' : 'FAIL'}'); -// } -// final payload = buildInspectionPayload(checkList); -// -// log('API Payload: $payload'); -// // call this as bottomsheet. -// // final result = await showModalBottomSheet>>( -// // context: context, -// // isScrollControlled: true, -// // backgroundColor: Colors.transparent, -// // builder: (_) => const InspectionChecklistBottomSheet(), -// // ); -// } -// -// bool _isPass(List list, String title) { -// return list -// .firstWhere( -// (e) => e.title == title, -// orElse: () => InspectionChecklistItem(status: 'FAIL'), -// ) -// .status == -// 'PASS'; -// } -// -// Map buildInspectionPayload(List checklist) { -// return { -// 'flagphysicalInspection': _isPass(checklist, 'Physical Inspection As Per Manufacturer Instructions'), -// 'flagFunctionPerformance': _isPass(checklist, 'Functional Performance As Per Results Of Manufacturer Recommended Check List'), -// 'flagGroundingResistance': _isPass(checklist, 'Grounding Resistance: Ω'), -// 'flagChassiss': _isPass(checklist, 'Chassis Leakage Current: µA'), -// 'flagLeadsLeakage': _isPass(checklist, 'Leads Leakage Current: µA'), -// 'flagAlert': _isPass(checklist, 'This Device Is Not Subject To Any Alert Or Recall Until The Date Of Preparation Of This Inspection Form'), -// }; -// } -// -// void applyInspectionFlagsToChecklist({ -// required List checklist, -// required TechnicalInspectionParentAssetModel model, -// }) { -// for (final item in checklist) { -// switch (item.title) { -// case 'Physical Inspection As Per Manufacturer Instructions': -// item.status = _statusFromBool(model.flagphysicalInspection); -// break; -// -// case 'Functional Performance As Per Results Of Manufacturer Recommended Check List': -// item.status = _statusFromBool(model.flagFunctionPerformance); -// break; -// -// case 'Grounding Resistance: Ω': -// item.status = _statusFromBool(model.flagGroundingResistance); -// break; -// -// case 'Chassis Leakage Current: µA': -// item.status = _statusFromBool(model.flagChassiss); -// break; -// -// case 'Leads Leakage Current: µA': -// item.status = _statusFromBool(model.flagLeadsLeakage); -// break; -// -// case 'This Device Is Not Subject To Any Alert Or Recall Until The Date Of Preparation Of This Inspection Form': -// item.status = _statusFromBool(model.flagAlert); -// break; -// } -// } -// } -// String _statusFromBool(bool? value) { -// return value == true ? 'PASS' : 'FAIL'; -// } -// } From 4bd055cfe852a4787955f56abdb2aa090903a15e Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Tue, 20 Jan 2026 11:00:43 +0300 Subject: [PATCH 35/35] merge conflict resolved. --- .../notification/firebase_notification_manger.dart | 7 +++---- lib/modules/cm_module/cm_detail_page.dart | 2 +- lib/modules/cx_module/chat/chat_widget.dart | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/controllers/notification/firebase_notification_manger.dart b/lib/controllers/notification/firebase_notification_manger.dart index b941e40e..243e8a57 100644 --- a/lib/controllers/notification/firebase_notification_manger.dart +++ b/lib/controllers/notification/firebase_notification_manger.dart @@ -13,9 +13,8 @@ import 'package:test_sa/modules/cm_module/cm_detail_page.dart'; import 'package:test_sa/modules/cx_module/survey/survey_page.dart'; import 'package:test_sa/modules/pm_module/ppm_wo/ppm_details_page.dart'; import 'package:test_sa/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart'; -import 'package:test_sa/modules/tm_module/tasks_wo/task_request_detail_view.dart'; -import 'package:test_sa/views/pages/device_transfer/device_transfer_details.dart'; -import 'package:test_sa/views/pages/user/gas_refill/gas_refill_details.dart'; +import 'package:test_sa/modules/tm_module/device_transfer/device_transfer_details.dart'; +import 'package:test_sa/modules/tm_module/tasks/task_request_detail_view.dart'; import 'package:test_sa/modules/tm_module/gas_refill/gas_refill_details.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; @@ -99,7 +98,7 @@ class FirebaseNotificationManger { switch (moduleId) { case 1: // cm - serviceClass = ServiceRequestDetailMain(requestId: requestNumber, moduleId: moduleId); + serviceClass = CMDetailPage(requestId: requestNumber, moduleId: moduleId); break; case 2: // gas refill serviceClass = GasRefillDetailsPage( diff --git a/lib/modules/cm_module/cm_detail_page.dart b/lib/modules/cm_module/cm_detail_page.dart index b9870847..15b1b503 100644 --- a/lib/modules/cm_module/cm_detail_page.dart +++ b/lib/modules/cm_module/cm_detail_page.dart @@ -42,7 +42,7 @@ class _CMDetailPageState extends State { @override void initState() { super.initState(); - _requestProvider = Provider.of(context, listen: false); + _requestProvider = Provider.of(context, listen: false); WidgetsBinding.instance.addPostFrameCallback((_) { Provider.of(context, listen: false).reset(); getInitialData(); diff --git a/lib/modules/cx_module/chat/chat_widget.dart b/lib/modules/cx_module/chat/chat_widget.dart index 43c5d5fc..8d5011e8 100644 --- a/lib/modules/cx_module/chat/chat_widget.dart +++ b/lib/modules/cx_module/chat/chat_widget.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; -import 'package:test_sa/modules/cm_module/service_request_detail_provider.dart'; +import 'package:test_sa/modules/cm_module/cm_detail_provider.dart'; import 'package:test_sa/new_views/common_widgets/custom_badge.dart'; import 'chat_page.dart'; @@ -59,13 +59,13 @@ class _ChatWidgetState extends State { ChatProvider cProvider = Provider.of(context, listen: false); if (cProvider.chatLoginResponse != null && cProvider.referenceID == widget.requestId) return; - String assigneeEmployeeNumber = widget.assigneeEmployeeNumber ?? Provider.of(context, listen: false).currentWorkOrder?.data?.assignedEmployee?.employeeId ?? ""; + String assigneeEmployeeNumber = widget.assigneeEmployeeNumber ?? Provider.of(context, listen: false).currentWorkOrder?.data?.assignedEmployee?.employeeId ?? ""; String myEmployeeId = widget.myLoginUserID ?? context.userProvider.user!.username!; String receiver = context.userProvider.isNurse ? assigneeEmployeeNumber : (context.userProvider.isEngineer - ? (widget.contactEmployeeINumber ?? Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!) + ? (widget.contactEmployeeINumber ?? Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!) : ""); cProvider.getUserAutoLoginTokenSilent(widget.moduleId, widget.requestId, widget.title, myEmployeeId, receiver, isMounted: mounted);