From 0a9dd86ce0e4e8015f587c71554cbc2faa0e2eee Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 7 Dec 2025 15:22:53 +0300 Subject: [PATCH 01/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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),