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 d51fb77c..66a0b6c2 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -1,12 +1,11 @@ class URLs { URLs._(); - static const String appReleaseBuildNumber = "28"; + static const String appReleaseBuildNumber = "32"; // 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 = "http://10.201.111.125:9495"; // temporary Server UAT url // static String _baseUrl = "$_host/mobile"; @@ -20,6 +19,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; @@ -67,8 +67,12 @@ 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'; @@ -410,8 +414,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'; @@ -422,7 +428,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"; @@ -430,7 +436,9 @@ 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=5007"; + static get endUserRejectionReasonLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=415"; + static get attachmentTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=5009"; //Medical Gas Request.. // 4004 diff --git a/lib/controllers/notification/firebase_notification_manger.dart b/lib/controllers/notification/firebase_notification_manger.dart index c01d9965..243e8a57 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'; @@ -13,6 +14,7 @@ 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/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'; @@ -88,6 +90,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 = CMDetailPage(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, @@ -101,21 +141,22 @@ class FirebaseNotificationManger { break; //these three request are same corrective maintenance.... case "3": - serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString())); + serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); break; case "8": - serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString())); + serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); break; case "11": - serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString())); + serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); 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/controllers/providers/api/all_requests_provider.dart b/lib/controllers/providers/api/all_requests_provider.dart index 29dca48b..86579328 100644 --- a/lib/controllers/providers/api/all_requests_provider.dart +++ b/lib/controllers/providers/api/all_requests_provider.dart @@ -140,6 +140,7 @@ class AllRequestsProvider extends ChangeNotifier { } if (context.userProvider.isQualityUser) { list = [10, 11]; + if (context.settingProvider.isUserFlowMedical) list.insert(0, 7); return list; } @@ -165,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 c09cb621..cc39807f 100644 --- a/lib/dashboard_latest/dashboard_provider.dart +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -106,11 +106,22 @@ class DashBoardProvider extends ChangeNotifier { isAllCountLoading = true; notifyListeners(); String url = ''; - 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: {}); @@ -233,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; @@ -241,22 +278,26 @@ class DashBoardProvider extends ChangeNotifier { Response response; String url = ''; - 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; - } + // 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); + try { Map body = {"pageNumber": pageNum, "pageSize": pageItemNumber}; @@ -372,6 +413,19 @@ 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; + } + 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)); tabs.add(CategoryTabs(label: 'Pending Acknowledgement', tag: 3, id: 1)); 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/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/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/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/extensions/enum_extensions.dart b/lib/extensions/enum_extensions.dart index 747e2080..24d0a479 100644 --- a/lib/extensions/enum_extensions.dart +++ b/lib/extensions/enum_extensions.dart @@ -121,7 +121,37 @@ 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, } + + +enum CostCenterType { + costCenter, + serialNumber, +} +enum AssetDeliveryStageEnum { + deliveryInspection, + technicalInspection, + endUserAcceptance, + other, +} diff --git a/lib/main.dart b/lib/main.dart index 6592c495..2fece980 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'; @@ -140,6 +141,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 {}); @@ -331,6 +333,7 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => PowerSupplyTypeProvider()), ChangeNotifierProvider(create: (_) => EndUserStatusLookupProvider()), ChangeNotifierProvider(create: (_) => EndUserRejectionReasonLookupProvider()), + ChangeNotifierProvider(create: (_) => AttachmentTypeLookupProvider()), ChangeNotifierProvider(create: (_) => MedicalGasOrderTypeProvider()), ChangeNotifierProvider(create: (_) => MedicalGasItemTypeProvider()), ChangeNotifierProvider(create: (_) => MedicalGasInspectionProvider()), 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/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/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/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/models/user.dart b/lib/models/user.dart index 2fd42598..17da87c9 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, @@ -108,8 +110,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; } @@ -146,6 +153,7 @@ class User { map['message'] = message; map['username'] = username; map['userID'] = userID; + map['employeeId'] = employeeId; map['email'] = email; map['password'] = password; map['token'] = token; @@ -207,6 +215,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/asset_delivery_module/helper_function.dart b/lib/modules/asset_delivery_module/helper_function.dart new file mode 100644 index 00000000..0cfb122e --- /dev/null +++ b/lib/modules/asset_delivery_module/helper_function.dart @@ -0,0 +1,73 @@ +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, bool viewOnly = false, 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, + viewOnly: viewOnly, + 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/asset_delivery_data_model.dart b/lib/modules/asset_delivery_module/models/asset_delivery_data_model.dart index 08de3111..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 @@ -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']; } @@ -303,6 +305,7 @@ class AssetDeliveryTableModel { status = json['status'] != null ? Lookup.fromJson(json['status']) : null; + } Map toJson() { 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/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/asset_delivery_page.dart b/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart index aad1d276..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,66 +76,64 @@ 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) { 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), ); }, ), @@ -248,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), @@ -323,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/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/attachment_view.dart b/lib/modules/asset_delivery_module/pages/attachment_view.dart index 0c22772e..2f4020e7 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,22 @@ 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; + bool viewOnly = false; + final List attachmentList; - const AssetDeliveryAttachmentView({ + AssetDeliveryAttachmentView({ super.key, - this.lineId, - this.requestId, + required this.attachmentList, + this.viewOnly=false, + this.attachmentType, this.tableItemId, - this.isHmg, }); @override @@ -52,15 +55,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/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..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,6 +1,3 @@ - -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; @@ -11,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'; @@ -48,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() { @@ -61,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) { @@ -79,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]); }); } @@ -103,102 +107,207 @@ 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( - itemCount: _list.length + 1, - shrinkWrap: true, - padding: EdgeInsets.all(widget.cardPadding ?? 16), - 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 CostCenterItemCard( - model: _list[index], - index: index, - isReadOnly: false, - isHmg: widget.isHmg, - requestId: widget.requestId, - onUpdate: (updateList) => _updateModel(index, updateList), - onRemove: () => _removeEntry(index), - ); - }, - ).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, - ); - } - }); - }), - ), - ], - ); + return provider.isLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Column( + children: [ + itemDetails(), + selectionButton(), + _selectedType == CostCenterType.costCenter ? _buildCostCenterList() : _buildSerialNumberList(), + 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: formModel.costCenters, serialNoList: formModel.serialNos) + .then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context); + assetDeliveryProvider.getDeliveryLinesListById( + itemId: widget.tableItemId, + ); + } + }); + }), + ), + ], + ); }), ); } -} -Widget itemDetails({required BuildContext context, String? itemNo, String? itemDescription}) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - 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, - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 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 { @@ -293,6 +402,56 @@ 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); + } +} + +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 36faf4bf..2d113140 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,27 +1,33 @@ +import 'dart:convert'; 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'; +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/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/asset_delivery_stage_tab_page.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'; @@ -85,12 +91,12 @@ class _DeliveryInspectionFormViewState 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( @@ -110,9 +116,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( @@ -124,32 +137,50 @@ 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, + buttonColor: AppColor.primary10, + label: 'Attachments'.addTranslation, + maxWidth: true, + onPressed: () { + HelperFunction.attachmentTap(context: context, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id); + }), + 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), + : 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, + ], + ), ), ], ), @@ -195,69 +226,132 @@ 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)); } 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, - ), + _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) ...[ 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: '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, @@ -267,52 +361,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, @@ -322,55 +395,120 @@ 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())) { + "Time From cannot be in the future".showToast; + return false; + } + if (to != null && from.isAfter(to)) { + "Time To cannot be after Time From".showToast; + return false; + } + } + if (to != null) { + if (to.isAfter(DateTime.now())) { + "Time To cannot be in the future".showToast; + return false; + } + if (from != null && to.isBefore(from)) { + "Timer To cannot be before from 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; + } + + 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); + + 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, @@ -418,13 +556,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 { @@ -440,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, + // ))); } } @@ -470,4 +687,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..434f3382 --- /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 ?? ''); + } + + @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 ?? ''; + emailController.text = value.employeeEmail ?? ''; + 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, + enable: widget.isHmg?false:true, + 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/delivery_inspection/oracle_user_autocomplete_field.dart b/lib/modules/asset_delivery_module/pages/delivery_inspection/oracle_user_autocomplete_field.dart index 5411081a..9c18de9b 100644 --- a/lib/modules/asset_delivery_module/pages/delivery_inspection/oracle_user_autocomplete_field.dart +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/oracle_user_autocomplete_field.dart @@ -26,25 +26,26 @@ class OracleUserAutoCompleteField extends StatefulWidget { class _AutoCompletePartsFieldState 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/end_user_asset_detail_card.dart b/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_asset_detail_card.dart index 838ba850..f023c938 100644 --- a/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_asset_detail_card.dart +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_asset_detail_card.dart @@ -90,10 +90,12 @@ class EndUserAssetDetailCard extends StatelessWidget { 'Replaced Asset No: ${assetModel.replacedAssetNo ?? '-'}', style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), ), - Text( - 'Rejection Reason: ${assetModel.rejectionReason?.name ?? '-'}', - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), - ), + //TODO Need to confirm with @Ahmed need to hide this or not + if (assetModel.status?.value == 2) + Text( + 'Rejection Reason: ${assetModel.rejectionReason?.name ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), Text( 'Description: ${assetModel.description ?? '-'}', style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), 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..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 @@ -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'; @@ -71,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 @@ -83,31 +84,41 @@ 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(), + : 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); + }), + ), ], ), ), @@ -156,12 +167,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/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 d48326f8..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 @@ -1,6 +1,229 @@ +// 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'; +// 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/lookup.dart'; +// import 'package:test_sa/modules/asset_delivery_module/models/end_user_asset_details_model.dart'; +// import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_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'; +// 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/single_item_drop_down_menu.dart'; +// 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; +// +// UpdateEndUserAssetDetailsView({Key? key, this.assetDetailsModel, this.costCenterName, this.tableId}) : super(key: key); +// +// @override +// 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,259 @@ 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; + model.receivedQty = widget.assetDetailsModel?.receivedQty; } + 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 _showReceivedQty { + if (_isAccessory && model.receivedQty != null) 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 (_showReceivedQty) ...[ + _receivedQtyField(), + 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 _receivedQtyField() { + return Container( + padding: const EdgeInsets.all(12), + width: double.infinity, + decoration: BoxDecoration( + color: AppColor.neutral40, + // color: AppColor.fieldBgColor(context), + borderRadius: BorderRadius.circular(12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Received Qty', style: AppTextStyles.textFieldLabelStyle), + 4.height, + Text(model.receivedQty.toString(), style: AppTextStyles.textFieldLabelStyle), + ], + ), + ); + + AppTextFormField( + labelText: 'Received Quantity'.addTranslation, + initialValue: model.receivedQty.toString(), + textAlign: TextAlign.center, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + enable: false, + style: Theme.of(context).textTheme.titleMedium, + ); + } + + 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/history_log_view.dart b/lib/modules/asset_delivery_module/pages/history_log_view.dart index 9aace55d..a5f6bf04 100644 --- a/lib/modules/asset_delivery_module/pages/history_log_view.dart +++ b/lib/modules/asset_delivery_module/pages/history_log_view.dart @@ -41,12 +41,12 @@ class _AssetDeliveryHistoryLogViewState 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 520b51a7..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 @@ -7,40 +7,33 @@ 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/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/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_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_inpection_asset_card_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_asset_card_view.dart index d5c75e54..b09eb878 100644 --- a/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_asset_card_view.dart +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_asset_card_view.dart @@ -75,6 +75,14 @@ class TechnicalInspectionAssetCard extends StatelessWidget { 'Asset Number: ${assetModel.assetNumber ?? '-'}', style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), ), + Text( + 'Model: ${assetModel.modelDefinition?.model ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Manufacturer: ${assetModel.modelDefinition?.manufacturer ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), Text( 'Serial Number: ${assetModel.serialNo ?? '-'}', style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), 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..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 @@ -8,9 +8,11 @@ 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'; +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'; @@ -78,95 +80,106 @@ 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, + ], + ), + ], + ) + : 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, + ], + ), + ), + ], + ), ), ); } - 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( @@ -227,12 +240,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 { @@ -247,12 +261,24 @@ 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 c95dd194..bdbec424 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 @@ -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/provider/asset_delivery_provider.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,17 +181,53 @@ 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); } } }), 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), controller: serialNoCtrl, initialValue: model.serialNo ?? '', + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, textAlign: TextAlign.center, labelStyle: AppTextStyles.textFieldLabelStyle, textInputType: TextInputType.number, @@ -346,7 +380,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); @@ -358,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) { @@ -423,6 +463,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..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 @@ -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'; @@ -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; @@ -216,20 +214,64 @@ 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), controller: serialNoCtrl, initialValue: model.serialNo ?? '', + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, textAlign: TextAlign.center, labelStyle: AppTextStyles.textFieldLabelStyle, textInputType: TextInputType.number, @@ -384,12 +426,16 @@ 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; 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); @@ -404,7 +450,7 @@ class _UpdateParentAssetViewState extends State { final result = await Navigator.push( context, MaterialPageRoute( - builder: (context) => InspectionChecklistBottomSheet( + builder: (context) => InspectionCheckListView( initialValues: checkList, ))); @@ -413,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) { @@ -479,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'; -// } -// } 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> 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,17 @@ 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 +429,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/modules/cm_module/cm_detail_page.dart b/lib/modules/cm_module/cm_detail_page.dart index cbf729ca..15b1b503 100644 --- a/lib/modules/cm_module/cm_detail_page.dart +++ b/lib/modules/cm_module/cm_detail_page.dart @@ -13,6 +13,7 @@ import 'package:test_sa/modules/cm_module/cm_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/modules/loan_module/pages/create_loan_request_page.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; @@ -23,8 +24,9 @@ import 'views/components/service_request_detail_view.dart'; class CMDetailPage extends StatefulWidget { final int requestId; + final int moduleId; - CMDetailPage({Key? key, required this.requestId}) : super(key: key); + CMDetailPage({Key? key, required this.requestId, required this.moduleId}) : super(key: key); @override _CMDetailPageState createState() { @@ -40,6 +42,7 @@ class _CMDetailPageState extends State { @override void initState() { super.initState(); + _requestProvider = Provider.of(context, listen: false); WidgetsBinding.instance.addPostFrameCallback((_) { Provider.of(context, listen: false).reset(); getInitialData(); @@ -89,84 +92,46 @@ class _CMDetailPageState 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 { - CMDetailProvider 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(); - } - }), - if (isNurse) - IconButton( - icon: 'qr'.toSvgAsset(height: 24, width: 24), - onPressed: () { - ServiceRequestBottomSheet.getQRCodeBottomSheet(context: context); - }, - ) - else ...[ - Selector( - selector: (_, myModel) => myModel.isLoading, // Selects only the userName - builder: (_, isLoading, __) { - if (isLoading) { - return const SizedBox(); - } else { - CMDetailProvider provider = Provider.of(context, listen: false); - int? statusValue = provider.currentWorkOrder?.data?.status?.value; - if (statusValue == 2) { - return Container( - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), - margin: const EdgeInsets.only(left: 2), - decoration: BoxDecoration(color: context.isDark ? AppColor.neutral50 : AppColor.neutral30, borderRadius: BorderRadius.circular(16)), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.add, size: 20, color: AppColor.black1E), - Text( - "Loan", - style: AppTextStyles.bodyText2.copyWith(color: AppColor.black1E), - ), - 4.width, - ], - ), - ).onPress(() { - Navigator.push(context, MaterialPageRoute(builder: (context) => CreateLoanRequestPage(workOrderId: widget.requestId))); - }); - } - return const SizedBox(); + Selector( + selector: (_, myModel) => myModel.isLoading, // Selects only the userName + builder: (_, isLoading, __) { + if (isLoading) { + return const SizedBox(); + } else { + CMDetailProvider 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.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!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, + contactEmployeeINumber: provider.currentWorkOrder?.data?.workOrderContactPerson.first.employeeId, + ); } - }), - IconButton( - icon: const Icon(Icons.home), - onPressed: () { - // stopTimer(); - Navigator.pop(context); - }, - ) - ], + return const SizedBox(); + } + }), + isNurse + ? IconButton( + icon: 'qr'.toSvgAsset( + height: 24, + width: 24, + ), + onPressed: () { + ServiceRequestBottomSheet.getQRCodeBottomSheet(context: context); + }, + ) + : IconButton( + icon: const Icon(Icons.home), + onPressed: () { + // stopTimer(); + Navigator.pop(context); + }, + ), ], ), body: DefaultTabController( diff --git a/lib/modules/cm_module/create_cm_request.dart b/lib/modules/cm_module/create_cm_request.dart index 1ae1d862..95c74d29 100644 --- a/lib/modules/cm_module/create_cm_request.dart +++ b/lib/modules/cm_module/create_cm_request.dart @@ -300,7 +300,7 @@ class _CreateCMRequestState extends State with TickerProviderSt 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/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/cx_module/chat/chat_api_client.dart b/lib/modules/cx_module/chat/chat_api_client.dart index b209db79..5fdc5b52 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(); @@ -81,8 +82,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", 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); @@ -94,6 +96,20 @@ class ChatApiClient { } } + Future resetCountApi(int moduleCode, int referenceNumber, String? employeeNumber) async { + final Map payload = { + 'moduleCode': "$moduleCode", + 'ReferenceNumber': "$referenceNumber", + }; + final Response response = await ApiClient().postJsonForResponse( + URLs.resetMessageCount, + payload, + token: chatLoginResponse!.token, + ); + return response.statusCode == 200; + } + + 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 8b8e05c9..fdd96dba 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'; @@ -29,6 +30,7 @@ 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 +68,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(); @@ -75,7 +80,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 +104,14 @@ 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); + 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)) @@ -607,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 136dd28b..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'; @@ -40,6 +41,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'; @@ -54,6 +56,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 { @@ -248,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 @@ -370,7 +377,19 @@ 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; + } } void updateUserChatHistoryStatusAsync(List data) { @@ -453,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!) { @@ -559,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/cx_module/chat/chat_widget.dart b/lib/modules/cx_module/chat/chat_widget.dart index a137245e..8d5011e8 100644 --- a/lib/modules/cx_module/chat/chat_widget.dart +++ b/lib/modules/cx_module/chat/chat_widget.dart @@ -1,9 +1,12 @@ + + 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/cm_detail_provider.dart'; +import 'package:test_sa/new_views/common_widgets/custom_badge.dart'; import 'chat_page.dart'; import 'chat_provider.dart'; @@ -40,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() { @@ -73,25 +78,33 @@ 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, 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/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/helper/chat_file_viewer.dart b/lib/modules/cx_module/chat/helper/chat_file_viewer.dart index fbd53eec..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) { @@ -127,8 +128,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; 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..80c6bfc4 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; + dynamic 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']; @@ -20,18 +23,20 @@ class ChatParticipantModel { } 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/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/create_equipment_internal_audit_form.dart b/lib/modules/internal_audit_module/pages/equipment_internal_audit/create_equipment_internal_audit_form.dart index 83ddb00c..638b7189 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..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 @@ -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 EquipmentInternalAuditDetailPage(auditId: requestDetails!.id!))); + Navigator.push(context, MaterialPageRoute(builder: (context) => EquipmentInternalAuditDetailPage(auditId: requestData!.id!))); }); } return Column( @@ -85,9 +84,11 @@ class EquipmentInternalAuditItemView extends StatelessWidget { 8.height, (requestDetails?.nameOfType ?? "Internal Audit Request").heading5(context), 8.height, + infoWidget(label: context.translation.requestNo, value: requestDetails!.requestNo, context: context), infoWidget(label: context.translation.assetNumber, value: requestDetails!.assetNo, context: context), infoWidget(label: context.translation.assetSN, value: requestDetails!.assetSN, context: context), infoWidget(label: context.translation.model, value: requestDetails!.model, context: context), + infoWidget(label: context.translation.site, value: requestDetails!.site, context: context), 8.height, Row( mainAxisSize: MainAxisSize.min, diff --git a/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_detail_page.dart b/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_detail_page.dart index 9f3804e4..848b5dcf 100644 --- a/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_detail_page.dart +++ b/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_detail_page.dart @@ -20,6 +20,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/requests/request_status.dart'; class SystemInternalAuditDetailPage extends StatefulWidget { static const String id = "/details-system-internal-audit"; @@ -79,17 +80,45 @@ class _SystemInternalAuditDetailPageState extends State SystemInternalAuditDetailPage(auditId: requestDetails!.id!))); + Navigator.push(context, MaterialPageRoute(builder: (context) => SystemInternalAuditDetailPage(auditId: requestData!.id!))); }); } return Column( @@ -85,9 +85,12 @@ class SystemInternalAuditItemView extends StatelessWidget { ), 8.height, (requestDetails?.nameOfType ?? "Internal Audit Request").heading5(context), + + infoWidget(label: context.translation.requestNo, value: requestDetails!.requestNo, context: context), infoWidget(label: context.translation.assetNumber, value: requestDetails!.assetNo, context: context), infoWidget(label: context.translation.assetSN, value: requestDetails!.assetSN, context: context), infoWidget(label: context.translation.model, value: requestDetails!.model, context: context), + infoWidget(label: context.translation.site, value: requestDetails!.site, context: context), 8.height, // infoWidget(label: context.translation.site, value: requestDetails!.site, context: context), 8.height, diff --git a/lib/modules/loan_module/models/loan_form_model.dart b/lib/modules/loan_module/models/loan_form_model.dart index b31df2e6..79d459ca 100644 --- a/lib/modules/loan_module/models/loan_form_model.dart +++ b/lib/modules/loan_module/models/loan_form_model.dart @@ -1,8 +1,7 @@ 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/models/new_models/traf_department.dart'; import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; class LoanFormModel { @@ -21,6 +20,8 @@ class LoanFormModel { Site? site; MedicalDepartmentModel? department; List? loanAttachment; + MappedSite? mappedSite; + TrafDepartment? mappedDepartment; LoanFormModel({ this.docName, @@ -38,6 +39,8 @@ class LoanFormModel { this.loanAttachment, this.site, this.department, + this.mappedSite, + this.mappedDepartment, }); //{ @@ -91,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/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; - - } 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..32616761 100644 --- a/lib/modules/loan_module/pages/create_loan_request_page.dart +++ b/lib/modules/loan_module/pages/create_loan_request_page.dart @@ -1,58 +1,42 @@ 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/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/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/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/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/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'; -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 +46,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(); @@ -70,11 +53,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,23 +78,55 @@ 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(), + '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, ], + ...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), onChange: (attachments) { attachments = attachments; @@ -251,10 +269,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"; @@ -263,12 +281,17 @@ class _CreateLoanRequestPageState extends State with Tick backgroundColor: AppColor.fieldBgColor(context), showAsBottomSheet: true, onSelect: (value) { - _loanFormModel.site = value; - setState(() {}); + if(value!=null){ + _loanFormModel.mappedSite = value; + _loanFormModel.mappedDepartment = null; + Provider.of(context,listen:false).getData(id:_loanFormModel.mappedSite?.id); + setState(() {}); + } + }, ), 8.height, - SingleItemDropDownMenu( + SingleItemDropDownMenu( context: context, title: context.translation.department, showShadow: false, @@ -277,11 +300,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(() {}); }, ), @@ -473,24 +496,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(); @@ -507,7 +514,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); 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/modules/tm_module/device_transfer/create_device_transfer_request.dart b/lib/modules/tm_module/device_transfer/create_device_transfer_request.dart index 26a9c10f..6b9ed8aa 100644 --- a/lib/modules/tm_module/device_transfer/create_device_transfer_request.dart +++ b/lib/modules/tm_module/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/modules/tm_module/device_transfer/device_transfer_details.dart b/lib/modules/tm_module/device_transfer/device_transfer_details.dart index b5761a69..6e843398 100644 --- a/lib/modules/tm_module/device_transfer/device_transfer_details.dart +++ b/lib/modules/tm_module/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/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'; @@ -22,8 +23,9 @@ import 'update_device_transfer.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: context.settingProvider.isUserFlowMedical && _model!.senderMachineStatusValue! != 4, + isReadOnly: _model!.senderMachineStatusValue! != 1, + requestId: widget.model.id!.toInt(), + assigneeEmployeeNumber: _model!.senderAssignedEmployeeNumber, + myLoginUserID: context.userProvider.user!.employeeId ?? 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: 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!, + myLoginUserID: context.userProvider.user!.employeeId ?? 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/modules/tm_module/gas_refill/gas_refill_details.dart b/lib/modules/tm_module/gas_refill/gas_refill_details.dart index a6e60491..c785a6a1 100644 --- a/lib/modules/tm_module/gas_refill/gas_refill_details.dart +++ b/lib/modules/tm_module/gas_refill/gas_refill_details.dart @@ -28,8 +28,9 @@ import 'update_gas_refill_request.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: context.settingProvider.isUserFlowMedical && _gasRefillModel.status!.value! != 0, + isReadOnly: _gasRefillModel.status!.value! != 1, + requestId: widget.model.id!.toInt(), + assigneeEmployeeNumber: _gasRefillModel.assignedEmployee?.employeeId!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, + contactEmployeeINumber: _gasRefillModel.gasRefillContactPerson!.first.employeeCode!, + ); + }) ], ), key: _scaffoldKey, diff --git a/lib/modules/tm_module/tasks/task_request_detail_view.dart b/lib/modules/tm_module/tasks/task_request_detail_view.dart index e5b5bdb5..1a60c0b9 100644 --- a/lib/modules/tm_module/tasks/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks/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/cm_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,31 @@ 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(); + if ((context.userProvider.isEngineer || context.userProvider.isNurse) && + _taskReqModel.assignedEngineer?.employeeId != null && + (_taskReqModel.taskJobContactPersons?.isNotEmpty ?? false)) { + return ChatWidget( + moduleId: widget.moduleId, + 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!.employeeId ?? context.userProvider.user!.username!, + contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId, + ); + } else + return const SizedBox(); + }) + ], + ), body: Consumer(builder: (context, taskProvider, child) { TaskData? taskModel = taskProvider.taskRequestModel; return taskProvider.isLoading diff --git a/lib/modules/tm_module/tasks/task_request_item_view.dart b/lib/modules/tm_module/tasks/task_request_item_view.dart index 3cc6bd05..2c164df3 100644 --- a/lib/modules/tm_module/tasks/task_request_item_view.dart +++ b/lib/modules/tm_module/tasks/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/common_widgets/app_bottom_nav_bar.dart b/lib/new_views/common_widgets/app_bottom_nav_bar.dart index f1c47f6d..4195d6e4 100644 --- a/lib/new_views/common_widgets/app_bottom_nav_bar.dart +++ b/lib/new_views/common_widgets/app_bottom_nav_bar.dart @@ -18,13 +18,12 @@ class AppBottomNavigationBar extends StatelessWidget { Widget build(BuildContext context) { bool isEngineer = (Provider.of(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), ]; 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, + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/new_views/pages/land_page/create_request_type_bottom_sheet.dart b/lib/new_views/pages/land_page/create_request_type_bottom_sheet.dart index 0bd10a54..f19edb97 100644 --- a/lib/new_views/pages/land_page/create_request_type_bottom_sheet.dart +++ b/lib/new_views/pages/land_page/create_request_type_bottom_sheet.dart @@ -113,12 +113,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", CreateCMRequest.id)); } @@ -135,7 +137,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)); list.add(CreateRequestModel('Loan Equipment Request'.addTranslation, "add_icon", CreateLoanRequestPage.id)); } return list; diff --git a/lib/new_views/pages/land_page/land_page.dart b/lib/new_views/pages/land_page/land_page.dart index 437a87dc..b024e501 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(); } 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 c134fecd..35253c86 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 @@ -79,9 +79,9 @@ class _AllRequestsFilterPageState extends State { if (isEngineer) { types[context.translation.recurrentWo] = 5; - types["Equipment Internal Audit".addTranslation] = 10; - types["System Internal Audit".addTranslation] = 11; - types["Medical Gas Inspection".addTranslation] = 14; + if (context.settingProvider.isUserFlowMedical) types["Equipment Internal Audit".addTranslation] = 10; + if (context.settingProvider.isUserFlowMedical) types["System Internal Audit".addTranslation] = 11; + if (context.settingProvider.isUserFlowMedical) types["Medical Gas Inspection".addTranslation] = 11; } if (context.settingProvider.isUserFlowMedical && isEngineer) { @@ -94,16 +94,24 @@ class _AllRequestsFilterPageState extends State { types[module.module!.name!] = module.module!.value!; } }); +//Hide traf + // if (!isEngineer) { + // types['TRAF'] = 9; + // } - if (!isEngineer) { - types['TRAF'] = 9; - } - - if (context.userProvider.isAssessor) { - types = {"TRAF": 9}; - } + // if (context.userProvider.isAssessor) { + // 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}; + if (context.settingProvider.isUserFlowMedical) types['Equipment Internal Audit'] = 10; + if (context.settingProvider.isUserFlowMedical) 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 = { 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 1823b10a..e28dca9c 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 @@ -62,14 +62,15 @@ 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')); //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(14, "Medical Gas Inspection")); } @@ -78,14 +79,22 @@ class _MyRequestsPageState extends State { if (context.userProvider.isAssessor) { requestsList = [ Request(null, context.translation.allWorkOrder), - Request(9, 'TRAF'), + // 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), - Request(10, 'Equipment Internal Audit'), - Request(11, 'System Internal Audit'), + if (context.settingProvider.isUserFlowMedical) Request(7, 'Recall and Alert'), + if (context.settingProvider.isUserFlowMedical) Request(10, 'Equipment Internal Audit'), + if (context.settingProvider.isUserFlowMedical) Request(11, 'System Internal Audit'), ]; } 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 008ce1e8..dfb12d40 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 1143487a..bd6ac0a7 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/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 f11d784c..b24a01cc 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: (_) => CMDetailPage(requestId: requestData!.id!))); + await Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(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: (_) => CMDetailPage(requestId: requestDetails!.id!))); + Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(requestId: requestDetails!.id!, moduleId: requestDetails!.transactionType!))); }); } 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/user/requests/pending_requests_screen.dart b/lib/views/pages/user/requests/pending_requests_screen.dart index b87af0d9..acacc179 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: (_) => CMDetailPage(requestId: pendingAssetServiceRequest.details![index].id ?? 0))); + Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(requestId: pendingAssetServiceRequest.details![index].id ?? 0, moduleId: moduleId))); }))); } } 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, ], 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 fbeeb9ce..e2973118 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,54 @@ 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(), + padding: EdgeInsets.zero, + 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 +429,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 +497,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..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,7 @@ 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'; @@ -18,8 +20,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 +37,96 @@ 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: EllipsizedText( + file.path.split("/").last, + style: AppTextStyles.bodyText.copyWith(color: AppColor.black10), + type: EllipsisType.start, // or EllipsisType.middle + ), + ), + 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, 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..678fdb0f 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.5.0+32 environment: sdk: ">=3.5.0 <4.0.0" @@ -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: