diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html new file mode 100644 index 00000000..d59d3b0f --- /dev/null +++ b/android/build/reports/problems/problems-report.html @@ -0,0 +1,663 @@ + + + + + + + + + + + + + Gradle Configuration Cache + + + +
+ +
+ Loading... +
+ + + + + + diff --git a/assets/images/history_icon.svg b/assets/images/history_icon.svg new file mode 100644 index 00000000..89ab3df8 --- /dev/null +++ b/assets/images/history_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/web_link_icon.svg b/assets/images/web_link_icon.svg new file mode 100644 index 00000000..9d77b3f7 --- /dev/null +++ b/assets/images/web_link_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index ed077246..eed578cf 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -1,7 +1,7 @@ class URLs { URLs._(); - static const String appReleaseBuildNumber = "31"; + static const String appReleaseBuildNumber = "32"; // static const host1 = "https://atomsm.hmg.com"; // production url static const host1 = "https://atomsmdev.hmg.com"; // local DEV url @@ -18,6 +18,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; @@ -66,9 +67,11 @@ class URLs { //service request new flow urls. static get nurseDashboardCountUrl => '$_baseUrl/ServiceRequest/GetDashboardNurseCount'; static get qaAuditorCount => '$_baseUrl/ServiceRequest/GetDashboardQAAuditorCount'; + static get commissioningEngineerCount => '$_baseUrl/ServiceRequest/GetDashboardCommissioningEngineerCount'; static get nurseDashboardDetailsUrl => '$_baseUrl/ServiceRequest/GetDashboardNurseDetails'; static get qaAuditorDetails => '$_baseUrl/ServiceRequest/GetDashboardQAAuditorDetails'; + static get commissioningEngineerDetails => '$_baseUrl/ServiceRequest/GetDashboardCommissioningEngineerDetails'; static get nurseRejectUrl => '$_baseUrl/ServiceRequest/NurseReject'; @@ -393,4 +396,61 @@ class URLs { static get getLoanById => '$_baseUrl/Loan/GetLoanById'; static get addLoan => '$_baseUrl/Loan/AddLoan'; + + //Asset Delivery Module + static get getAssetDeliveryDetailsById => '$_baseUrl/AssetDeliveryExternal/GetAssetDeliveryExternalById'; + + static get getAssetDeliveryTableListById => '$_baseUrl/AssetDeliveryExternalDelivery/GetDeliveriesByMasterId'; + + static get addNewDelivery => '$_baseUrl/AssetDeliveryExternalDelivery/AddNewDelivery'; + + static get changeDeliveryStatusToCancel => '$_baseUrl/AssetDeliveryExternalDelivery/ChangeStatusDeliveryToCancel'; + + static get changeDeliveryStatus => '$_baseUrl/AssetDeliveryExternalDelivery/ChangeStatusDelivery'; + + static get getAssetDeliveryInspectionData => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/GetAssetDeliveryExternalDeliveryInspectionByDeliveryId'; + + static get getOracleUserList => '$_baseUrl/AssetDeliveryExternal/GetEmployeesFromOracle'; + + 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'; + static get getTechnicalInspectionByDetailId => '$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetTechnicalInspectionByDetailId'; + static get powerSupplyTypeLookUp => "$_baseUrl/Lookups/GetLookup?lookupEnum=527"; + static get saveTechnicalInspection => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/SaveTechnicalInspection"; + static get getTechnicalInspectionParentData => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetTechnicalInspectionItemByCostCenterId"; + static get getAssetFromInternalTechnicalInspection => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetAssetFromInternal"; + static get getAssetDetailsByAssetId => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetAssetByIdFromInternal"; + static get saveTechnicalInspectionAsset => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/SaveTechnicalInspectionItem"; + static get getAttachments => "$_baseUrl/AssetDeliveryExternalDeliveryAttachment/GetAttachmentsBasedOnType"; + static get saveAttachments => "$_baseUrl/AssetDeliveryExternalDeliveryAttachment/Save"; + //End user Acceptance... + static get getEndUserAcceptanceCostCenter => "$_baseUrl/AssetDeliveryEndUserAcceptance/GetEndUserAcceptanceCostCenter"; + static get getCostCenterAssetList => "$_baseUrl/AssetDeliveryEndUserAcceptance/GetItemByCostCenter"; + static get saveEndUserFormData => "$_baseUrl/AssetDeliveryEndUserAcceptance/SaveMaster"; + static get saveEndUserAssetData => "$_baseUrl/AssetDeliveryEndUserAcceptance/SaveDetail"; + static get endUserStatusLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=534"; + // static get endUserRejectionReasonLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=5007"; + static get endUserRejectionReasonLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=415"; + static get attachmentTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=5009"; + + //Medical Gas Request.. +// 4004 + static get orderTypeLookUp => "$_baseUrl/Lookups/GetLookup?lookupEnum=4004"; + // static get itemTypeLookUp => "$_baseUrl/Lookups/GetLookup?lookupEnum=4005"; + static get itemTypeLookUp => "$_baseUrl/MedicalGas/GetItemTypeLookup?lookupEnum=4002"; + static get getMedicalGasSupplierList => "$_baseUrl/MedicalGas/GetMedicalGasSupplierAsync"; + static get getMedicalGasInfoBySupplier => "$_baseUrl/MedicalGas/GetMedicalGasInfoBySupplierAsync"; + static get addMedicalGasRequest => "$_baseUrl/MedicalGas/AddUpdateMedicalGasRequestAsync"; + static get getMedicalGasById => "$_baseUrl/MedicalGas/GetMedicalGasRequestByIdAsync"; + static get updateDeliveryNote => "$_baseUrl/MedicalGas/MedicalGasRequestDeliveryNotesAsync"; + + + } diff --git a/lib/controllers/notification/firebase_notification_manger.dart b/lib/controllers/notification/firebase_notification_manger.dart index bc011827..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,13 +141,13 @@ 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())), moduleId: 3); diff --git a/lib/controllers/providers/api/all_requests_provider.dart b/lib/controllers/providers/api/all_requests_provider.dart index 5036ea9c..86579328 100644 --- a/lib/controllers/providers/api/all_requests_provider.dart +++ b/lib/controllers/providers/api/all_requests_provider.dart @@ -166,7 +166,11 @@ class AllRequestsProvider extends ChangeNotifier { if (context.userProvider.isNurse) { list.add(9); // } - list.add(12); // loan module + list.add(12); + if (context.userProvider.isCommissioningEngineer) { + list = [13]; + } + // loan module return list; } diff --git a/lib/controllers/providers/api/devices_provider.dart b/lib/controllers/providers/api/devices_provider.dart index 3d031a61..ab196c11 100644 --- a/lib/controllers/providers/api/devices_provider.dart +++ b/lib/controllers/providers/api/devices_provider.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'package:flutter/cupertino.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -10,6 +11,7 @@ import 'package:test_sa/models/device/asset_by_id_model.dart'; import 'package:test_sa/models/device/asset_search.dart'; import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/user.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/technical_inpection_asset_model.dart'; import '../../../models/device/asset.dart'; @@ -38,6 +40,7 @@ class AssetProvider extends ChangeNotifier { List _devices = []; List _searchDevices = []; + List technicalInspectionAsset= []; List get devices => _devices; @@ -99,6 +102,49 @@ class AssetProvider extends ChangeNotifier { return response.statusCode; } + Future getAssetsForAssetDelivery({DeviceSearch? search, bool isSearchBy = false, int? lineId}) async { + if (_loading == true) return -2; + _loading = true; + notifyListeners(); + late Response response; + try { + final Map body = { + "pageSize": isSearchBy ? searchPageItemNumber : pageItemNumber, + "pageNumber": isSearchBy ? (searchDevices.length / searchPageItemNumber).ceil() + 1 : devices.length ~/ pageItemNumber + 1, + + ///Uncommit this after testing... + "assetDeliveryExternalDetailId": lineId, + "assetName": search?.assetName, + + // "assetDeliveryExternalDetailId": 2, + // "assetName": "Treatment Center Intego - Dental Chair", + "assetNumber": search?.assetNo, + "model": search?.model, + "manufacturer": search?.manufacturer, + }; + response = await ApiManager.instance.post(URLs.getAssetFromInternalTechnicalInspection, body: body); + } catch (error) { + _loading = false; + _stateCode = -1; + notifyListeners(); + return -1; + } + _stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + List equipmentListJson = json.decode(response.body)["data"]; + var dList = equipmentListJson.map((asset) => TechnicalInspectionAssetBasicInfoModel.fromJson(asset)).toList() ?? []; + log('dlist ${dList.length}'); + + technicalInspectionAsset.addAll(dList); + nextPage = true; + } else { + nextPage = false; + } + _loading = false; + notifyListeners(); + return response.statusCode; + } + Future getAssetById(int assetId, AppLocalizations appLocalizations) async { late Response response; try { diff --git a/lib/controllers/providers/api/user_provider.dart b/lib/controllers/providers/api/user_provider.dart index ad64c8b8..d82a6b2d 100644 --- a/lib/controllers/providers/api/user_provider.dart +++ b/lib/controllers/providers/api/user_provider.dart @@ -47,6 +47,7 @@ class UserProvider extends ChangeNotifier { bool get isAssessor => user!.type == UsersTypes.assessor || user!.type == UsersTypes.assessorTl; bool get isQualityUser => user!.type == UsersTypes.qualityUser; + bool get isCommissioningEngineer => user!.type == UsersTypes.commissioningEngineer; VerifyOtpModel _verifyOtpModel = VerifyOtpModel(); SwipeTransaction _swipeTransactionModel = SwipeTransaction(); diff --git a/lib/dashboard_latest/dashboard_provider.dart b/lib/dashboard_latest/dashboard_provider.dart index f1a92f54..cc39807f 100644 --- a/lib/dashboard_latest/dashboard_provider.dart +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -106,13 +106,22 @@ class DashBoardProvider extends ChangeNotifier { isAllCountLoading = true; notifyListeners(); String url = ''; - if (usersType == UsersTypes.qualityUser) { - url = URLs.qaAuditorCount; - } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { - url = URLs.engineerDashboardCountUrl; - } else { - url = URLs.nurseDashboardCountUrl; - } + + // if (usersType == UsersTypes.qualityUser) { + // url = URLs.qaAuditorCount; + // } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { + // url = URLs.engineerDashboardCountUrl; + // } else { + // url = URLs.nurseDashboardCountUrl; + // } + final urlMap = { + UsersTypes.qualityUser: URLs.qaAuditorCount, + UsersTypes.commissioningEngineer: URLs.commissioningEngineerCount, + UsersTypes.engineer: URLs.engineerDashboardCountUrl, + UsersTypes.assessor: URLs.engineerDashboardCountUrl, + UsersTypes.assessorTl: URLs.engineerDashboardCountUrl, + }; + url = urlMap[usersType] ?? URLs.nurseDashboardCountUrl; Response response; try { response = await ApiManager.instance.post(url, body: {}); @@ -235,6 +244,32 @@ class DashBoardProvider extends ChangeNotifier { } } + String getDashboardUrl({ + required UsersTypes usersType, + int? status, + int? tabId, + }) { + switch (usersType) { + case UsersTypes.qualityUser: + return URLs.qaAuditorDetails; + case UsersTypes.commissioningEngineer: + return URLs.commissioningEngineerDetails; + case UsersTypes.engineer: + case UsersTypes.assessor: + case UsersTypes.assessorTl: + if (status == 0) { + return URLs.engineerDashboardUpcoming; + } else if (status == 1 && tabId == 1) { + return URLs.engineerDashboardNotAssignDetails; + } else { + // status == 1 && tabId == 2 OR any other case + return URLs.engineerDashboardDetailsUrl; + } + default: + return URLs.nurseDashboardDetailsUrl; + } + } + Future getRequestDetail({bool showLoader = true, required UsersTypes usersType, int? status, bool isHighPriority = false, bool isOverdue = false, String? date, int? tabId}) async { if (showLoader) { isDetailLoading = showLoader; @@ -243,25 +278,26 @@ class DashBoardProvider extends ChangeNotifier { Response response; String url = ''; + // if (usersType == UsersTypes.qualityUser) { + // url = URLs.qaAuditorDetails; + // } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { + // //need to check pagination for not assigned task @waseem. + // + // //these checks are to call different apis for dashboard for engineer... + // if (status == 0) { + // url = URLs.engineerDashboardUpcoming; + // } else if (status == 1 && tabId == 1) { + // url = URLs.engineerDashboardNotAssignDetails; + // } else if (status == 1 && tabId == 2) { + // url = URLs.engineerDashboardDetailsUrl; + // } else { + // url = URLs.engineerDashboardDetailsUrl; + // } + // } else { + // url = URLs.nurseDashboardDetailsUrl; + // } + url = getDashboardUrl(usersType: usersType, status: status, tabId: tabId); - if (usersType == UsersTypes.qualityUser) { - url = URLs.qaAuditorDetails; - } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { - //need to check pagination for not assigned task @waseem. - - //these checks are to call different apis for dashboard for engineer... - if (status == 0) { - url = URLs.engineerDashboardUpcoming; - } else if (status == 1 && tabId == 1) { - url = URLs.engineerDashboardNotAssignDetails; - } else if (status == 1 && tabId == 2) { - url = URLs.engineerDashboardDetailsUrl; - } else { - url = URLs.engineerDashboardDetailsUrl; - } - } else { - url = URLs.nurseDashboardDetailsUrl; - } try { Map body = {"pageNumber": pageNum, "pageSize": pageItemNumber}; @@ -383,6 +419,12 @@ class CategoryTabs { tabs.add(CategoryTabs(label: 'Closed', tag: 3, id: 4)); return tabs; } + if (userType == UsersTypes.commissioningEngineer) { + tabs.add(CategoryTabs(label: 'Open', tag: 1, id: 2)); + tabs.add(CategoryTabs(label: 'In Progress', tag: 2, id: 3)); + tabs.add(CategoryTabs(label: 'Closed', tag: 3, id: 4)); + return tabs; + } tabs.add(CategoryTabs(label: 'Open', tag: 1, id: 2)); tabs.add(CategoryTabs(label: 'In Progress', tag: 2, id: 3)); diff --git a/lib/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/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 c6d10640..24d0a479 100644 --- a/lib/extensions/enum_extensions.dart +++ b/lib/extensions/enum_extensions.dart @@ -121,3 +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 aedb7794..20701c5c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,6 +27,12 @@ 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_last_calls_provider.dart'; 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'; +import 'package:test_sa/modules/asset_delivery_module/provider/power_supply_type_provider.dart'; import 'package:test_sa/modules/asset_inventory_module/provider/asset_inventory_provider.dart'; import 'package:test_sa/modules/cm_module/cm_detail_provider.dart'; import 'package:test_sa/modules/cm_module/create_cm_request.dart'; @@ -45,6 +51,9 @@ import 'package:test_sa/modules/loan_module/provider/loan_period_provider.dart'; import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; import 'package:test_sa/modules/loan_module/provider/medical_department_provider.dart'; import 'package:test_sa/modules/medical_gas_inspection/create_medical_gas_request_page.dart'; +import 'package:test_sa/modules/medical_gas_inspection/providers/item_type_provider.dart'; +import 'package:test_sa/modules/medical_gas_inspection/providers/medical_gas_inspection_provider.dart'; +import 'package:test_sa/modules/medical_gas_inspection/providers/order_type_provider.dart'; import 'package:test_sa/modules/tm_module/gas_refill/create_gas_refill_request.dart'; import 'package:test_sa/modules/tm_module/gas_refill/update_gas_refill_request.dart'; import 'package:test_sa/modules/traf_module/create_traf_request_page.dart'; @@ -113,6 +122,7 @@ import 'dashboard_latest/dashboard_provider.dart'; import 'modules/internal_audit_module/pages/equipment_internal_audit/update_equipment_internal_audit_page.dart'; import 'modules/internal_audit_module/provider/internal_audit_checklist_provider.dart'; import 'modules/cx_module/survey/survey_provider.dart'; +import 'modules/medical_gas_inspection/providers/medical_gas_supplier_provider.dart'; import 'modules/tm_module/device_transfer/create_device_transfer_request.dart'; import 'modules/tm_module/device_transfer/device_filter_screen.dart'; import 'modules/tm_module/device_transfer/device_search_screen.dart'; @@ -134,6 +144,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 {}); @@ -320,6 +331,16 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => LoanProvider()), ChangeNotifierProvider(create: (_) => LoanPeriodProvider()), ChangeNotifierProvider(create: (_) => MedicalDepartmentProvider()), + ChangeNotifierProvider(create: (_) => AssetDeliveryProvider()), + ChangeNotifierProvider(create: (_) => CostCenterProvider()), + ChangeNotifierProvider(create: (_) => PowerSupplyTypeProvider()), + ChangeNotifierProvider(create: (_) => EndUserStatusLookupProvider()), + ChangeNotifierProvider(create: (_) => EndUserRejectionReasonLookupProvider()), + ChangeNotifierProvider(create: (_) => AttachmentTypeLookupProvider()), + ChangeNotifierProvider(create: (_) => MedicalGasOrderTypeProvider()), + ChangeNotifierProvider(create: (_) => MedicalGasItemTypeProvider()), + ChangeNotifierProvider(create: (_) => MedicalGasInspectionProvider()), + ChangeNotifierProvider(create: (_) => MedicalGasSupplierProvider()), ChangeNotifierProvider(create: (_) => DemoProvider()), ChangeNotifierProvider(create: (_) => DemoPeriodLookupProvider()), ], @@ -389,6 +410,7 @@ class MyApp extends StatelessWidget { UpdateEquipmentInternalAuditPage.id: (_) => UpdateEquipmentInternalAuditPage(), UpdateSystemInternalAuditPage.id: (_) => UpdateSystemInternalAuditPage(), CreateLoanRequestPage.id: (_) => CreateLoanRequestPage(), + CreateMedicalGasRequestPage.id: (_) => CreateMedicalGasRequestPage(), // SwipeSuccessView.routeName: (_) => const SwipeSuccessView(), // SwipeHistoryView.routeName: (_) => const SwipeHistoryView(), diff --git a/lib/models/all_requests_and_count_model.dart b/lib/models/all_requests_and_count_model.dart index e980e164..95f19030 100644 --- a/lib/models/all_requests_and_count_model.dart +++ b/lib/models/all_requests_and_count_model.dart @@ -128,6 +128,8 @@ class RequestsDetails { String? date; String? siteTransferFrom; String? siteTransferTo; + String? poInternal; + String? poExternal; int? transactionType; int? numberOfAssets; int? numberOfSites; @@ -156,6 +158,8 @@ class RequestsDetails { this.code, this.siteTransferFrom, this.siteTransferTo, + this.poInternal, + this.poExternal, this.date, this.transactionType}); @@ -182,6 +186,8 @@ class RequestsDetails { assetTransferTo = json['assetTransferTo']; code = json['code']; date = json['date']; + poInternal = json['poInternal']; + poExternal = json['poExternal']; siteTransferFrom = json['siteTransferFrom']; siteTransferTo = json['siteTransferTo']; transactionType = json['transactionType']; diff --git a/lib/models/device/asset_search.dart b/lib/models/device/asset_search.dart index f311b208..1a70196b 100644 --- a/lib/models/device/asset_search.dart +++ b/lib/models/device/asset_search.dart @@ -1,6 +1,7 @@ import 'package:test_sa/models/lookup.dart'; class DeviceSearch { + DeviceSearch( {this.code, this.assetSerialNumber, 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/generic_attachment_model.dart b/lib/models/generic_attachment_model.dart index 1d3cd5ea..c0c2b4cf 100644 --- a/lib/models/generic_attachment_model.dart +++ b/lib/models/generic_attachment_model.dart @@ -8,7 +8,6 @@ class GenericAttachmentModel { String ?createdBy; String? originalName; GenericAttachmentModel.fromJson(Map json) { - print('created by here is ${json['createdBy']}'); id = json['id']; name = json['name']; createdBy = json['createdBy']; 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/dashboard_detail.dart b/lib/models/new_models/dashboard_detail.dart index 093019b2..93c868a1 100644 --- a/lib/models/new_models/dashboard_detail.dart +++ b/lib/models/new_models/dashboard_detail.dart @@ -55,6 +55,9 @@ class Data { String? assetNumber; String? requestTypeName; String? requestNo; + String? requestType; + String? poInternal; + String? poExternal; int? transactionNo; int? numberOfSites; int? numberOfAssets; @@ -75,6 +78,9 @@ class Data { this.assetNumber, this.requestTypeName, this.requestNo, + this.requestType, + this.poExternal, + this.poInternal, this.transactionNo}); Data.fromJson(Map json) { @@ -86,6 +92,7 @@ class Data { isHighPriority = json['isHighPriority']; assetName = json['assetName']; sessionType = json['sessionType']; + requestType = json['requestType']; numberOfSites = json['numberOfSites']; numberOfAssets = json['numberOfAssets']; assetNumber = json['assetNumber']; @@ -93,6 +100,8 @@ class Data { requestNo = json['requestNo']; rejectReason = json['rejectReason']; transactionNo = json['transactionNo']; + poInternal = json['poInternal']; + poExternal = json['poExternal']; nameOfType = json['nameOfType']; } diff --git a/lib/models/user.dart b/lib/models/user.dart index 5eac8486..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,10 +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; } @@ -148,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; @@ -209,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 new file mode 100644 index 00000000..15ece8ca --- /dev/null +++ b/lib/modules/asset_delivery_module/models/asset_delivery_data_model.dart @@ -0,0 +1,386 @@ +import 'package:test_sa/models/lookup.dart'; + +class AssetDeliveryDataModel { + int? id; + bool? isHMG; + int? operatingUnitId; + String? operatingUnit; + String? clientName; + int? poNumberId; + int? poNumber; + SiteModel? site; + Lookup? paymentTerm; + num? paymentTermAmount; + num? deliveryAmount; + num? total; + num? totalInvoice; + String? edd; + double? warrentyPeriod; + + AssetDeliveryInternalMaster? assetDeliveryInternalMaster; + PurchaseOrderMaster? purchaseOrderMaster; + List? assetDeliveryExternalDetails; + List? assetDeliveryExternalDeliveries; + + AssetDeliveryDataModel(); + + AssetDeliveryDataModel.fromJson(Map json) { + id = json['id']; + isHMG = json['isHMG']; + operatingUnitId = json['operatingUnitId']; + clientName = json['clientName']; + operatingUnit = json['operatingUnit']; + poNumberId = json['poNumberId']; + poNumber = json['poNumber']; + total = json['total']; + totalInvoice = json['totalInvoice']; + edd = json['edd']; + deliveryAmount = json['deliveryAmount']; + site = json['site'] != null + ? SiteModel.fromJson(json['site']) + : null; + paymentTerm = json['paymentTerm'] != null + ? Lookup.fromJson(json['paymentTerm']) + : null; + paymentTermAmount = json['paymentTermAmount']; + warrentyPeriod = json['warrentyPeriod']; + + assetDeliveryInternalMaster = json['assetDeliveryInternalMaster'] != null + ? AssetDeliveryInternalMaster.fromJson(json['assetDeliveryInternalMaster']) + : null; + purchaseOrderMaster = json['purchaseOrderMaster'] != null + ? PurchaseOrderMaster.fromJson(json['purchaseOrderMaster']) + : null; + + if (json['assetDeliveryExternalDetails'] != null) { + assetDeliveryExternalDetails = []; + json['assetDeliveryExternalDetails'].forEach((v) { + assetDeliveryExternalDetails!.add(AssetDeliveryExternalDetail.fromJson(v)); + }); + } + + if (json['assetDeliveryExternalDeliveries'] != null) { + assetDeliveryExternalDeliveries = []; + json['assetDeliveryExternalDeliveries'].forEach((v) { + assetDeliveryExternalDeliveries!.add(AssetDeliveryTableModel.fromJson(v)); + }); + } + } + + Map toJson() { + final data = {}; + + data['id'] = id; + data['isHMG'] = isHMG; + data['operatingUnitId'] = operatingUnitId; + data['operatingUnit'] = operatingUnit; + data['clientName'] = clientName; + data['poNumberId'] = poNumberId; + data['poNumber'] = poNumber; + data['total'] = total; + data['edd'] = edd; + data['totalInvoice'] = totalInvoice; + data['deliveryAmount'] = deliveryAmount; + data['site'] = site; + data['paymentTermAmount'] = paymentTermAmount; + data['warrentyPeriod'] = warrentyPeriod; + + if (paymentTerm != null) data['paymentTerm'] = paymentTerm!.toJson(); + if (assetDeliveryInternalMaster != null) { + data['assetDeliveryInternalMaster'] = + assetDeliveryInternalMaster!.toJson(); + } + if (assetDeliveryExternalDetails != null) { + data['assetDeliveryExternalDetails'] = + assetDeliveryExternalDetails!.map((v) => v.toJson()).toList(); + } + if (assetDeliveryExternalDeliveries != null) { + data['assetDeliveryExternalDeliveries'] = + assetDeliveryExternalDeliveries!.map((v) => v.toJson()).toList(); + } + + return data; + } +} + + +class AssetDeliveryInternalMaster { + int? id; + int? operatingUnitId; + String? operatingUnit; + int? poNumberId; + int? poNumber; + String? supplier; + String? supplierSite; + String? type; + String? buyer; + num? total; + String? currency; + String? remark; + String? status; + String? paymentTerms; + double? warrantyPeriod; + String? assetDeliveryStatus; + String? statusDocument; + String? remarkDocument; + String? edd; + + AssetDeliveryInternalMaster(); + + AssetDeliveryInternalMaster.fromJson(Map json) { + id = json['id']; + operatingUnitId = json['operatingUnitId']; + operatingUnit = json['operatingUnit']; + poNumberId = json['poNumberId']; + poNumber = json['poNumber']; + supplier = json['supplier']; + supplierSite = json['supplierSite']; + type = json['type']; + buyer = json['buyer']; + total = json['total']; + currency = json['currency']; + remark = json['remark']; + status = json['status']; + paymentTerms = json['paymentTerms']; + warrantyPeriod = json['warrantyPeriod']; + assetDeliveryStatus = json['assetDeliveryStatus']; + statusDocument = json['statusDocument']; + remarkDocument = json['remarkDocument']; + edd = json['edd']; + } + + Map toJson() { + return { + 'id': id, + 'operatingUnitId': operatingUnitId, + 'operatingUnit': operatingUnit, + 'poNumberId': poNumberId, + 'poNumber': poNumber, + 'supplier': supplier, + 'supplierSite': supplierSite, + 'type': type, + 'buyer': buyer, + 'total': total, + 'currency': currency, + 'remark': remark, + 'status': status, + 'paymentTerms': paymentTerms, + 'warrantyPeriod': warrantyPeriod, + 'assetDeliveryStatus': assetDeliveryStatus, + 'statusDocument': statusDocument, + 'remarkDocument': remarkDocument, + 'edd': edd, + }; + } +} + +class AssetDeliveryExternalDetail { + int? id; + AssetDeliveryInternalDetail? assetDeliveryInternalDetail; + String? itemNumber; + String? lineType; + String? unitMeasure; + num? qtyOrdered; + num? qtyReceived; + num? qtyBilled; + num? qtyCancelled; + num? unitPrice; + num? unitPriceWithTax; + String? itemDescription; + + AssetDeliveryExternalDetail(); + + AssetDeliveryExternalDetail.fromJson(Map json) { + id = json['id']; + itemNumber = json['itemNumber']; + lineType = json['lineType']; + unitMeasure = json['unitMeasure']; + qtyOrdered = json['qtyOrdered']; + qtyReceived = json['qtyReceived']; + qtyBilled = json['qtyBilled']; + qtyCancelled = json['qtyCancelled']; + unitPrice = json['unitPrice']; + unitPriceWithTax = json['unitPriceWithTax']; + itemDescription = json['itemDescription']; + + assetDeliveryInternalDetail = + json['assetDeliveryInternalDetail'] != null + ? AssetDeliveryInternalDetail.fromJson( + json['assetDeliveryInternalDetail']) + : null; + } + + Map toJson() { + return { + 'id': id, + 'itemNumber': itemNumber, + 'lineType': lineType, + 'unitMeasure': unitMeasure, + 'qtyOrdered': qtyOrdered, + 'qtyReceived': qtyReceived, + 'qtyBilled': qtyBilled, + 'qtyCancelled': qtyCancelled, + 'unitPrice': unitPrice, + 'unitPriceWithTax': unitPriceWithTax, + 'itemDescription': itemDescription, + 'assetDeliveryInternalDetail': + assetDeliveryInternalDetail?.toJson(), + }; + } +} + +class AssetDeliveryInternalDetail { + int? id; + int? lineId; + String? itemNumber; + String? itemDescription; + String? unitMeasure; + num? qtyOrdered; + num? qtyReceived; + num? qtyBilled; + num? qtyCancelled; + num? unitPrice; + num? unitPriceWithTax; + Lookup? itemCategory; + num? totalAssets; + num? childPerAsset; + + AssetDeliveryInternalDetail(); + + AssetDeliveryInternalDetail.fromJson(Map json) { + id = json['id']; + lineId = json['lineId']; + itemNumber = json['itemNumber']; + itemDescription = json['itemDescription']; + unitMeasure = json['unitMeasure']; + qtyOrdered = json['qtyOrdered']; + qtyReceived = json['qtyReceived']; + qtyBilled = json['qtyBilled']; + qtyCancelled = json['qtyCancelled']; + unitPrice = json['unitPrice']; + unitPriceWithTax = json['unitPriceWithTax']; + itemCategory = json['itemCategory'] != null + ? Lookup.fromJson(json['itemCategory']) + : null; + totalAssets = json['totalAssets']; + childPerAsset = json['childPerAsset']; + } + + Map toJson() { + return { + 'id': id, + 'lineId': lineId, + 'itemNumber': itemNumber, + 'itemDescription': itemDescription, + 'unitMeasure': unitMeasure, + 'qtyOrdered': qtyOrdered, + 'qtyReceived': qtyReceived, + 'qtyBilled': qtyBilled, + 'qtyCancelled': qtyCancelled, + 'unitPrice': unitPrice, + 'unitPriceWithTax': unitPriceWithTax, + 'itemCategory': itemCategory, + 'totalAssets': totalAssets, + 'childPerAsset': childPerAsset, + }; + } +} +class AssetDeliveryTableModel { + int? id; + Lookup? status; + String? number; + num? deliveryAmount; + num? invoiceAmount; + bool? isContainsTechnicalInspection; + + AssetDeliveryTableModel(); + + AssetDeliveryTableModel.fromJson(Map json) { + id = json['id']; + number = json['number']; + deliveryAmount = json['deliveryAmount']; + invoiceAmount = json['invoiceAmount']; + isContainsTechnicalInspection = json['isContainsTechnicalInspection']; + + status = json['status'] != null + ? Lookup.fromJson(json['status']) + : null; + + } + + Map toJson() { + return { + 'id': id, + 'number': number, + 'deliveryAmount': deliveryAmount, + 'invoiceAmount': invoiceAmount, + 'isContainsTechnicalInspection': isContainsTechnicalInspection, + 'status': status?.toJson(), + }; + } +} + +class PurchaseOrderMaster { + int? id; + num? poNumber; + int? siteId; + String? siteName; + num? amount; + String? remarks; + String? statuss; + List? assetDeliveryPurchaseOrderDetails; + + PurchaseOrderMaster({ + this.id, + this.poNumber, + this.siteId, + this.siteName, + this.amount, + this.remarks, + this.statuss, + this.assetDeliveryPurchaseOrderDetails, + }); + + PurchaseOrderMaster.fromJson(Map json) { + id = json['id']; + poNumber = json['poNumber']; + siteId = json['siteId']; + siteName = json['siteName']; + amount = json['amount']; + remarks = json['remarks']; + statuss = json['statuss']; + + if (json['assetDeliveryPurchaseOrderDetails'] != null && + json['assetDeliveryPurchaseOrderDetails'] is List) { + assetDeliveryPurchaseOrderDetails = + (json['assetDeliveryPurchaseOrderDetails'] as List) + .map((e) => PurchaseOrderDetails.fromJson(e)) + .toList(); + } else { + assetDeliveryPurchaseOrderDetails = null; + } + } +} + +class SiteModel{ + int? id; + String? siteName; + + SiteModel({ + this.id, + this.siteName, + }); + + SiteModel.fromJson(Map json) { + id = json['id']; + siteName = json['siteName']; + } +} +///Check this model if required and whats the parm +class PurchaseOrderDetails { + // No fields provided in your JSON – placeholder + PurchaseOrderDetails(); + + PurchaseOrderDetails.fromJson(Map json); +} + diff --git a/lib/modules/asset_delivery_module/models/cost_center_model.dart b/lib/modules/asset_delivery_module/models/cost_center_model.dart new file mode 100644 index 00000000..0cacd34a --- /dev/null +++ b/lib/modules/asset_delivery_module/models/cost_center_model.dart @@ -0,0 +1,57 @@ +import 'package:test_sa/models/lookup.dart'; + +class CostCenterModel { + int? id; + int? assetDeliveryExternalDetailId; + Lookup? costCenter; + String? acceptedBy; + String? costCenterName; + DateTime? acceptanceDate; + String? acceptedByEmail; + String? acceptedBySignature; + double? quantity; + + CostCenterModel({ + this.id, + this.assetDeliveryExternalDetailId, + this.costCenter, + this.acceptedBy, + this.costCenterName, + this.acceptanceDate, + this.acceptedByEmail, + this.acceptedBySignature, + this.quantity, + }); + + CostCenterModel.fromJson(Map json) { + costCenterName = json['costCenter'] ?? json['costCenterName']; + acceptanceDate = json['acceptanceDate'] != null ? DateTime.tryParse(json['acceptanceDate']) : null; + acceptedBySignature = json['acceptedBySignature']; + acceptedByEmail = json['acceptedByEmail']; + quantity = json['qty'] ?? json['costCenterQty']; + acceptedBy = json['acceptedBy']; + costCenter = json['costCenter'] != null ? Lookup(name: json['costCenter']) : null; + id = json['id'] ?? json['costCenterId']; + assetDeliveryExternalDetailId = json['assetDeliveryExternalDetailId']; + } + + Map toJson() { + final Map data = {}; + + data['id'] = id ?? 0; + data['qty'] = quantity; + data['costCenter'] = costCenterName; + return data; + } + + Map toEndUserAcceptanceFormJson() { + final Map data = {}; + data['assetDeliveryExternalDeliveryId'] = assetDeliveryExternalDetailId; + data['costCenterName'] = costCenterName; + data['acceptedBy'] = acceptedBy; + data['acceptanceDate'] = acceptanceDate?.toIso8601String(); + data['acceptedByEmail'] = acceptedByEmail; + data['acceptedBySignature'] = acceptedBySignature; + 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 new file mode 100644 index 00000000..4d73b496 --- /dev/null +++ b/lib/modules/asset_delivery_module/models/delivery_inspection_form_model.dart @@ -0,0 +1,57 @@ +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; + String? deliveryInspectionApprovedBy; + + DeliveryInspectionFormModel({ + this.assetDeliveryExternalDeliveryId, + this.deliveryInspectionInspectedBy, + this.inspectedByModel, + this.approvedByModel, + this.thirdApprovalModel, + this.deliveryInspectionInspectedDateFrom, + this.deliveryInspectionInspectedDateTo, + this.totalTime, + this.deliveryInspectionApprovedBy, + }); + + DeliveryInspectionFormModel.fromJson(Map json) { + assetDeliveryExternalDeliveryId = json['assetDeliveryExternalDeliveryId']; + deliveryInspectionInspectedBy = json['deliveryInspectionInspectedBy']; + 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, + 'deliveryInspectionInspectedDateFrom': deliveryInspectionInspectedDateFrom?.toIso8601String(), + 'deliveryInspectionInspectedDateTo': deliveryInspectionInspectedDateTo?.toIso8601String(), + 'totalTime': totalTime, + '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/delivery_line_model.dart b/lib/modules/asset_delivery_module/models/delivery_line_model.dart new file mode 100644 index 00000000..d715c8c1 --- /dev/null +++ b/lib/modules/asset_delivery_module/models/delivery_line_model.dart @@ -0,0 +1,61 @@ +class DeliveryLineModel { + int? id; + int? assetDeliveryInternalDetailId; + String? itemNumber; + String? itemDescription; + num? qty; + num? remainingQty; + num? usingQtyInThisDelivery; + num? unitPrice; + num? total; + String? unitOfMeasure; + String? lineType; + String? itemCategory; + + DeliveryLineModel({ + this.id, + this.assetDeliveryInternalDetailId, + this.itemNumber, + this.itemDescription, + this.qty, + this.remainingQty, + this.usingQtyInThisDelivery, + this.unitPrice, + this.total, + this.unitOfMeasure, + this.lineType, + this.itemCategory, + }); + + DeliveryLineModel.fromJson(Map json) { + id = json['id']; + assetDeliveryInternalDetailId = json['assetDeliveryInternalDetailId']; + itemNumber = json['itemNumber']; + itemDescription = json['itemDescription']; + qty = json['qty']; + remainingQty = json['remainingQty']; + usingQtyInThisDelivery = json['usingQtyInThisDelivery']; + unitPrice = json['unitPrice']; + total = json['total']; + unitOfMeasure = json['unitOfMeasure']; + lineType = json['lineType']; + itemCategory = json['itemCategory']; + } + + Map toJson() { + return { + 'id': id, + 'assetDeliveryInternalDetailId': assetDeliveryInternalDetailId, + 'itemNumber': itemNumber, + 'itemDescription': itemDescription, + 'qty': qty, + 'remainingQty': remainingQty, + 'usingQtyInThisDelivery': usingQtyInThisDelivery, + 'unitPrice': unitPrice, + 'total': total, + 'unitOfMeasure': unitOfMeasure, + 'lineType': lineType, + 'itemCategory': itemCategory, + }; + } +} diff --git a/lib/modules/asset_delivery_module/models/end_user_asset_details_model.dart b/lib/modules/asset_delivery_module/models/end_user_asset_details_model.dart new file mode 100644 index 00000000..242cf70a --- /dev/null +++ b/lib/modules/asset_delivery_module/models/end_user_asset_details_model.dart @@ -0,0 +1,71 @@ +import 'package:test_sa/models/lookup.dart'; + +class EndUserAssetDetailsModel { + int? id; + int? typeId; + int? assetDeliveryExternalDeliveryId; + String? type; + String? itemNumber; + String? costCenterName; + Lookup? status; + Lookup? rejectionReason; + String? description; + double? receivedQty; + num? rejectedQty; + String? assetNumber; + String? serialNo; + String? assetType; + String? replacedAssetNo; + + EndUserAssetDetailsModel( + {this.costCenterName, + this.rejectedQty, + this.description, + this.id, + this.typeId, + this.status, + this.rejectionReason, + this.assetType, + this.assetNumber, + this.serialNo, + this.itemNumber, + this.type, + this.receivedQty, + this.replacedAssetNo, + this.assetDeliveryExternalDeliveryId}); + + EndUserAssetDetailsModel.fromJson(Map json) { + id = json['id']; + typeId = json['typeId']; + type = json['type']; + itemNumber = json['itemNumber']; + status = json['status'] != null ? Lookup.fromJson(json['status']) : null; + rejectionReason = json['rejectReason'] != null ? Lookup.fromJson(json['rejectReason']) : null; + description = json['description']; + receivedQty = json['receivedQty']; + rejectedQty = json['rejectedQty']; + assetNumber = json['assetNumber']; + serialNo = json['serialNo']; + assetType = json['assetType']; + replacedAssetNo = json['replacedAssetNo']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['assetDeliveryExternalDeliveryId'] = assetDeliveryExternalDeliveryId; + data['costCenterName'] = costCenterName; + data['typeId'] = typeId; + data['type'] = type; + data['itemNumber'] = itemNumber; + data['statusId'] = status?.id; + // data['status'] = status; + // data['rejectionReason'] = rejectionReason; + data['rejectReasonId'] = rejectionReason?.id; + data['description'] = description; + data['receivedQty'] = receivedQty; + data['rejectedQty'] = rejectedQty; + + return data; + } +} diff --git a/lib/modules/asset_delivery_module/models/history_model.dart b/lib/modules/asset_delivery_module/models/history_model.dart new file mode 100644 index 00000000..67fc4e11 --- /dev/null +++ b/lib/modules/asset_delivery_module/models/history_model.dart @@ -0,0 +1,34 @@ +import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/models/new_models/work_order_detail_model.dart'; + +class AssetDeliveryHistoryModel { + WorkOrderAssignedEmployee? user; + Lookup? status; + DateTime? createdDate; + String ?timeDifference; + + AssetDeliveryHistoryModel({ + this.user, + this.status, + this.createdDate, + this.timeDifference, + }); + + AssetDeliveryHistoryModel.fromJson(Map json) { + user = json['user'] != null ? WorkOrderAssignedEmployee.fromJson(json['user']) : null; + status = json['status'] != null ? Lookup.fromJson(json['status']) : null; + createdDate = json['createdDate'] != null ? DateTime.tryParse(json['createdDate']) : null; + timeDifference = ''; + } + + Map toJson() { + final Map data = {}; + if (user != null) { + data['user'] = user!.toJson(); + } + if (status != null) { + data['status'] = status!.toJson(); + } + return data; + } +} 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/oracle_user_model.dart b/lib/modules/asset_delivery_module/models/oracle_user_model.dart new file mode 100644 index 00000000..90ed9ee3 --- /dev/null +++ b/lib/modules/asset_delivery_module/models/oracle_user_model.dart @@ -0,0 +1,26 @@ +class OracleUserModel { + String? employeeNumber; + String? fullName; + String? job; + String? employeeMobileNumber; + String? employeeExtensionNumber; + String? employeeEmail; + + OracleUserModel({ + this.employeeNumber, + this.fullName, + this.job, + this.employeeMobileNumber, + this.employeeExtensionNumber, + this.employeeEmail, + }); + + OracleUserModel.fromJson(Map json) { + employeeNumber = json['employeeNumber']; + fullName = json['fullName']; + job = json['job']; + employeeMobileNumber = json['employeeMobileNumber']; + employeeExtensionNumber = json['employeeExtensionNumber']; + employeeEmail = json['employeeEmail']; + } +} diff --git a/lib/modules/asset_delivery_module/models/technical_inpection_asset_model.dart b/lib/modules/asset_delivery_module/models/technical_inpection_asset_model.dart new file mode 100644 index 00000000..7404b585 --- /dev/null +++ b/lib/modules/asset_delivery_module/models/technical_inpection_asset_model.dart @@ -0,0 +1,61 @@ +class TechnicalInspectionAssetBasicInfoModel { + int? id; + String? assetNumber; + String? assetType; + String? assetOrigin; + String? assetName; + String? model; + String? manufacturer; + String? site; + String? building; + String? floor; + String? department; + String? room; + + TechnicalInspectionAssetBasicInfoModel({ + this.id, + this.assetNumber, + this.assetType, + this.assetOrigin, + this.assetName, + this.model, + this.manufacturer, + this.site, + this.building, + this.floor, + this.department, + this.room, + }); + + TechnicalInspectionAssetBasicInfoModel.fromJson(Map json) { + id = json['id']; + assetNumber = json['assetNumber']; + assetType = json['assetType']; + assetOrigin = json['assetOrigin']; + assetName = json['assetName']; + model = json['model']; + manufacturer = json['manufacturer']; + site = json['site']; + building = json['building']; + floor = json['floor']; + department = json['department']; + room = json['room']; + } + + Map toJson() { + return { + 'id': id, + 'assetNumber': assetNumber, + 'assetType': assetType, + 'assetOrigin': assetOrigin, + 'assetName': assetName, + 'model': model, + 'manufacturer': manufacturer, + 'site': site, + 'building': building, + 'floor': floor, + 'department': department, + 'room': room, + }; + } +} 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 new file mode 100644 index 00000000..fafcc277 --- /dev/null +++ b/lib/modules/asset_delivery_module/models/technical_inspection_asset_model.dart @@ -0,0 +1,215 @@ +import 'dart:developer'; + +import 'package:test_sa/models/device/supplier.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/room_model.dart'; +import 'package:test_sa/models/new_models/site.dart'; + +import '../../../models/device/model_definition.dart'; +import '../../../models/new_models/department.dart'; + +class TechnicalInspectionAssetModel { + num? id; + num? tableItemId; + num? lineId; + num? assetDeliveryInternalDetailAssetId; + num? assetDeliveryInternalDetailAssetChildId; + num? costCenterId; + String? assetNumber; + String? status; + Lookup? assetOrigin; + Lookup? replacedAsset; + AssetModelDefination? modelDefinition; + Supplier? supplier; + Lookup? assetType; // Now nullable + Site? site; // Now nullable + Building? building; // Now nullable + Floor? floor; // Now nullable + Department? department; // Now nullable + Rooms? room; + String? serialNo; + String? systemId; + DateTime? installationDate; + List? childAssetList; + bool? flagphysicalInspection; + bool? flagFunctionPerformance; + bool? flagGroundingResistance; + bool? flagChassiss; + bool? flagLeadsLeakage; + bool? flagAlert; + + TechnicalInspectionAssetModel({ + this.id, + this.assetDeliveryInternalDetailAssetId, + this.assetDeliveryInternalDetailAssetChildId, + this.tableItemId, + this.status, + this.lineId, + this.costCenterId, + this.assetNumber, + this.assetOrigin, + this.assetType, + this.replacedAsset, + this.modelDefinition, + this.supplier, + this.site, + this.building, + this.floor, + this.department, + this.room, + this.serialNo, + this.systemId, + this.installationDate, + this.childAssetList, + this.flagphysicalInspection, + this.flagFunctionPerformance, + this.flagGroundingResistance, + this.flagChassiss, + this.flagLeadsLeakage, + this.flagAlert, + }); + + TechnicalInspectionAssetModel.fromJson(Map json) { + id = json['id']; + assetDeliveryInternalDetailAssetId = json['assetDeliveryInternalDetailAssetId']; + assetDeliveryInternalDetailAssetChildId = json['assetDeliveryInternalDetailAssetChildId']; + assetNumber = json['assetNumber']; + assetOrigin = json['assetOrigin'] != null ? Lookup.fromJson(json['assetOrigin']) : null; + replacedAsset = json['replacedAsset'] != null ? Lookup.fromJson(json['replacedAsset']) : null; + modelDefinition = json['modelDefinition'] != null ? AssetModelDefination.fromJson(json['modelDefinition']) : null; + supplier = json['supplier'] != null ? Supplier.fromJson(json['supplier']) : null; + assetType = json['assetType'] != null ? Lookup.fromJson(json['assetType']) : null; + site = json['site'] != null ? Site.fromJson(json['site']) : null; + building = json['building'] != null ? Building.fromJson(json['building']) : null; + floor = json['floor'] != null ? Floor.fromJson(json['floor']) : null; + department = json['department'] != null ? Department.fromJson(json['department']) : null; + room = json['room'] != null ? Rooms.fromJson(json['room']) : null; + serialNo = json['serialNo']; + systemId = json['systemId']; + installationDate = json['installationDate'] != null ? DateTime.tryParse(json['installationDate']) : null; + childAssetList = + json['assetDeliveryInternalDetailAssetChild'] != null ? (json['assetDeliveryInternalDetailAssetChild'] as List).map((e) => TechnicalInspectionAssetModel.fromJson(e)).toList() : []; + //Need to keep one parm from backend + // childAssetList = json['assetDeliveryExternalDeliveryLineTechnicalInspectionItemChild'] != null + // ? (json['assetDeliveryExternalDeliveryLineTechnicalInspectionItemChild'] as List).map((e) => TechnicalInspectionAssetModel.fromJson(e)).toList() + // : []; + flagphysicalInspection = json['flagphysicalInspection']; + flagFunctionPerformance = json['flagFunctionPerformance']; + flagGroundingResistance = json['flagGroundingResistance']; + flagChassiss = json['flagChassiss']; + flagLeadsLeakage = json['flagLeadsLeakage']; + flagAlert = json['flagAlert']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['assetDeliveryInternalDetailAssetId'] = assetDeliveryInternalDetailAssetId; + data['assetDeliveryExternalDeliveryId'] = tableItemId; + data['assetDeliveryExternalDetailId'] = lineId; + data['costCenterId'] = costCenterId; + data['assetNumber'] = assetNumber; + data['assetOriginId'] = assetOrigin?.id; + data['assetTypeId'] = assetType?.id; + data['replacedAssetId'] = replacedAsset?.id; + data['modelDefinitionId'] = modelDefinition?.modelDefinitionId; + data['supplierId'] = supplier?.id; + data['siteId'] = site?.id; + data['buildingId'] = building?.id; + data['floorId'] = floor?.id; + data['departmentId'] = department?.id; + data['roomId'] = room?.id; + data['serialNo'] = serialNo; + data['systemId'] = systemId; + data['installationDate'] = installationDate?.toIso8601String(); + data['assetDeliveryInternalDetailAssetChild'] = childAssetList?.map((e) => e.toChildJson()).toList(); + data['flagphysicalInspection'] = flagphysicalInspection; + data['flagFunctionPerformance'] = flagFunctionPerformance; + data['flagGroundingResistance'] = flagGroundingResistance; + data['flagChassiss'] = flagChassiss; + data['flagLeadsLeakage'] = flagLeadsLeakage; + data['flagAlert'] = flagAlert; + return data; + } + + Map toChildJson() { + final Map data = {}; + data['id'] = id ?? 0; + data['assetDeliveryInternalDetailAssetChildId'] = assetDeliveryInternalDetailAssetChildId; + data['assetNumber'] = assetNumber; + data['assetOriginId'] = assetOrigin?.id; + data['assetTypeId'] = assetType?.id; + data['replacedAssetId'] = replacedAsset?.id; + data['modelDefinitionId'] = modelDefinition?.modelDefinitionId; + data['supplierId'] = supplier?.id; + data['siteId'] = site?.id; + data['buildingId'] = building?.id; + data['floorId'] = floor?.id; + data['departmentId'] = department?.id; + data['roomId'] = room?.id; + data['serialNo'] = serialNo; + data['systemId'] = systemId; + data['installationDate'] = installationDate?.toIso8601String(); + data['flagphysicalInspection'] = flagphysicalInspection; + data['flagFunctionPerformance'] = flagFunctionPerformance; + data['flagGroundingResistance'] = flagGroundingResistance; + data['flagChassiss'] = flagChassiss; + data['flagLeadsLeakage'] = flagLeadsLeakage; + data['flagAlert'] = flagAlert; + return data; + } +} + +class InspectionChecklistItem { + String? title; + String? value; + String? status; // "PASS" or "FAIL" + + InspectionChecklistItem({this.title, this.status, this.value}); + + InspectionChecklistItem.fromJson(Map json) { + title = json['title']; + value = json['value']; + status = json['status']; + } + + Map toJson() { + return { + 'title': title, + 'value': value, + 'status': status, + }; + } +} + +class AssetModelDefination { + int? modelDefinitionId; + String? assetName; + String? model; + String? manufacturer; + + AssetModelDefination({ + this.modelDefinitionId, + this.assetName, + this.model, + this.manufacturer, + }); + + AssetModelDefination.fromJson(Map json) { + modelDefinitionId = json['modelDefinitionId']; + assetName = json['assetName']; + model = json['model']; + manufacturer = json['manufacturer']; + } + + Map toJson() { + final Map data = {}; + data['modelDefinitionId'] = modelDefinitionId; + data['assetName'] = assetName; + data['model'] = model; + data['manufacturer'] = manufacturer; + return data; + } +} diff --git a/lib/modules/asset_delivery_module/models/technical_inspection_form_model.dart b/lib/modules/asset_delivery_module/models/technical_inspection_form_model.dart new file mode 100644 index 00000000..cbde7191 --- /dev/null +++ b/lib/modules/asset_delivery_module/models/technical_inspection_form_model.dart @@ -0,0 +1,53 @@ +import 'dart:developer'; + +import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart'; + +class TechnicalInspectionFormModel { + Lookup? powerSupplyType; + num? powerReading; + num? maxCurrent; + num? voltage; + num? frequency; + List? costCenters; + int? tableItemId; + int? lineId; + + TechnicalInspectionFormModel({ + this.powerSupplyType, + this.powerReading, + this.maxCurrent, + this.voltage, + this.frequency, + this.costCenters, + this.lineId, + this.tableItemId, + }); + + TechnicalInspectionFormModel.fromJson(Map json) { + powerSupplyType = json["powerSupplyType"] == null ? null : Lookup.fromJson(json["powerSupplyType"]); + powerReading = json['powerReading']; + maxCurrent = json['maxCurrent']; + voltage = json['voltage']; + frequency = json['frequency']; + if (json['costCenters'] != null) { + costCenters = []; + json['costCenters'].forEach((v) { + costCenters!.add(CostCenterModel.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = {}; + data['powerSupplyTypeId'] = powerSupplyType?.id; + data['assetDeliveryExternalDeliveryId'] = tableItemId; + data['assetDeliveryExternalDetailId'] = lineId; + data['powerReading'] = powerReading; + data['maxCurrent'] = maxCurrent; + data['voltage'] = voltage; + data['frequency'] = frequency; + // data['costCenters'] = costCenters != null ? costCenters!.map((v) => v.toJson()).toList() : []; + return data; + } +} diff --git a/lib/modules/asset_delivery_module/pages/asset_delivery_item_view.dart b/lib/modules/asset_delivery_module/pages/asset_delivery_item_view.dart new file mode 100644 index 00000000..ff02233c --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/asset_delivery_item_view.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/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/all_requests_and_count_model.dart'; +import 'package:test_sa/models/new_models/dashboard_detail.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_page.dart'; +import 'package:test_sa/modules/asset_inventory_module/pages/asset_inventory_page.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; + +class AssetDeliveryItemView extends StatelessWidget { + final Data? requestData; + final RequestsDetails? requestDetails; + final bool showShadow; + + const AssetDeliveryItemView({Key? key, this.requestData, this.requestDetails, this.showShadow = true}) : super(key: key); + + @override + Widget build(BuildContext context) { + if (requestData != null) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 8.height, + (requestData?.typeTransaction ?? 'Asset Delivery').heading5(context), + infoWidget(label: 'PO Internal'.addTranslation, value: requestData?.poInternal ?? '-', context: context), + infoWidget(label: 'PO External'.addTranslation, value: requestData?.poExternal ?? '-', context: context), + // infoWidget(label: 'No of Sites'.addTranslation, value: requestData?.numberOfSites != null ? requestData?.numberOfSites.toString() : '-', context: context), + 8.height, + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.translation.viewDetails, + style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)), + ), + 4.width, + Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14) + ], + ), + ], + ).toShadowContainer(context, withShadow: showShadow).onPress(() async { + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => AssetDeliveryPage( + requestId: requestData!.id!, + poInternal: requestData?.poInternal, + poExternal: requestData?.poExternal, + ))); + }); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 8.height, + (requestDetails?.nameOfType ?? 'Asset Delivery').heading5(context), + infoWidget(label: 'PO Internal'.addTranslation, value: requestDetails?.poInternal ?? '-', context: context), + infoWidget(label: 'PO External'.addTranslation, value: requestDetails?.poExternal ?? '-', context: context), + 8.height, + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.translation.viewDetails, + style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)), + ), + 4.width, + Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14) + ], + ), + ], + ).toShadowContainer(context, withShadow: showShadow).onPress(() async { + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => AssetDeliveryPage( + requestId: requestDetails!.id!, + poInternal: requestDetails?.poInternal, + poExternal: requestDetails?.poExternal, + ))); + }); + } + + Widget infoWidget({required String label, String? value, required BuildContext context}) { + if (value != null && value.isNotEmpty) { + return '$label: $value'.bodyText(context); + } + return const SizedBox(); + } +} diff --git a/lib/modules/asset_delivery_module/pages/asset_delivery_line_card_view.dart b/lib/modules/asset_delivery_module/pages/asset_delivery_line_card_view.dart new file mode 100644 index 00000000..5337f457 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/asset_delivery_line_card_view.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.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/delivery_line_model.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; + +class AssetDeliveryLineCard extends StatelessWidget { + DeliveryLineModel deliveryLineModel; + bool isViewOnly; + VoidCallback? editPress; + bool isTechnical; + + AssetDeliveryLineCard({ + super.key, + required this.isViewOnly, + required this.deliveryLineModel, + this.isTechnical = false, + this.editPress, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + //need to check this @waseem. + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Item Category: ${deliveryLineModel.itemCategory ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + if (!isViewOnly) + 'edit_icon'.toSvgAsset().onPress(() { + if (editPress != null) { + editPress!(); + } + }), + ], + ), + Text( + 'Item No: ${deliveryLineModel.itemNumber ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Item Description: ${deliveryLineModel.itemDescription ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Quantity: ${deliveryLineModel.qty ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + + if(!isTechnical)...[ + Text( + 'Remaining Quantity: ${deliveryLineModel.remainingQty ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Quantity of this Delivery: ${deliveryLineModel.usingQtyInThisDelivery ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + ], + + Text( + 'Measure Unit: ${deliveryLineModel.unitOfMeasure ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Unit price: ${deliveryLineModel.unitPrice ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Line Type: ${deliveryLineModel.lineType ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Total: ${deliveryLineModel.total ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12); + } +} diff --git a/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart b/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart new file mode 100644 index 00000000..a4ce0983 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/asset_delivery_page.dart @@ -0,0 +1,364 @@ +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/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'; +import 'package:test_sa/modules/asset_delivery_module/pages/history_log_view.dart'; +import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.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/default_app_bar.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; + +class AssetDeliveryPage extends StatefulWidget { + final int? requestId; + final String? poInternal; + final String? poExternal; + + const AssetDeliveryPage({ + Key? key, + this.requestId, + this.poInternal, + this.poExternal, + }) : super(key: key); + + @override + State createState() => _AssetDeliveryPageState(); +} + +class _AssetDeliveryPageState extends State { + AssetDeliveryDataModel? dataModel; + AssetDeliveryProvider? assetDeliveryProvider; + bool loading = false; + + @override + void initState() { + assetDeliveryProvider = Provider.of(context, listen: false); + WidgetsBinding.instance.addPostFrameCallback((_) { + getDetailsById(); + }); + super.initState(); + } + + 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 + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + appBar: DefaultAppBar( + title: 'Asset Delivery'.addTranslation, + onBackPress: () { + Navigator.pop(context); + }, + ), + body: Consumer( + builder: (context, provider, child) { + final dataModel = provider.assetDeliveryDataModel; + if (loading) { + return const CircularProgressIndicator(color: AppColor.primary10).center; + } + if (dataModel == null) { + return const NoDataFound().center; + } + 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, + ), + ), + ], + ).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), + ); + }, + ), + ); + } + + TextStyle infoTextStyle(context) => AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120); + + Widget requestDetailCard(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'PO No. Internal'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + widget.poInternal ?? '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + Text( + 'Client Name'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + dataModel?.clientName ?? '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + // Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Checkbox( + // value: dataModel!.isHMG, + // activeColor: AppColor.neutral120, + // materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + // visualDensity: const VisualDensity(horizontal: -4, vertical: -3), + // onChanged: (value) {}, + // ), + // 8.width, + // Text( + // 'HMG'.addTranslation, + // style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + // ), + // ], + // ), + 8.height, + if (dataModel?.isHMG == true) ...[ + Text( + 'Operation Units'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + dataModel?.operatingUnit ?? '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + ] else ...[ + Text( + 'Site'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + dataModel?.site?.siteName ?? '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + ], + 8.height, + Text( + 'PO No.'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + //Need to verify this with backend ... + // widget.poInternal?? '-', + Text( + dataModel?.isHMG == true + ? dataModel?.poNumber != null + ? dataModel!.poNumber.toString() + : '-' + : dataModel?.poNumber != null + ? dataModel!.poNumber.toString() + : '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + Text( + 'Warranty Period'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + dataModel?.warrentyPeriod != null ? dataModel!.warrentyPeriod.toString() : '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + Text( + 'Payment Term'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + dataModel?.paymentTerm != null ? dataModel!.paymentTerm!.name.toString() : '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + + Text( + 'Payment Terms Amount'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + dataModel?.paymentTermAmount != null ? dataModel!.paymentTermAmount.toString() : '-', + 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), + ), + 8.height, + Text( + dataModel?.total != null ? dataModel!.total.toString() : '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + Text( + 'Total Invoice'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + dataModel?.totalInvoice != null ? dataModel!.totalInvoice.toString() : '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + ], + ); + } + + Widget assetDeliveryTableList({required BuildContext context, required AssetDeliveryProvider assetDeliveryProvider}) { + List assetDeliveryTableList = assetDeliveryProvider.assetDeliveryTableList; + return assetDeliveryProvider.isDeliveryTableListLoading + ? SizedBox(height: 200.toScreenHeight, child: const CircularProgressIndicator(color: AppColor.primary10).center) + : assetDeliveryTableList.isEmpty + ? const NoDataFound().center + : ListView.separated( + itemCount: assetDeliveryTableList.length, + separatorBuilder: (_, __) => 8.height, + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (listContext, itemIndex) { + AssetDeliveryTableModel model = assetDeliveryTableList[itemIndex]; + return AssetDeliveryTableCard( + deliveryTableModel: model, + onCardTap: () { + //status new + if (model.status?.value == 1) { + AssetDeliveryBottomSheet.changeStatusBottomSheet( + context: context, + cancelStatusTap: () async { + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await assetDeliveryProvider.changeDeliveryStatusToCancel(itemId: model.id).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context); + await assetDeliveryProvider.getAssetDeliveryTableListById(requestId: widget.requestId); + } + }); + }, + statusChangeTap: () async { + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await assetDeliveryProvider.changeDeliveryStatus(itemId: model.id).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context); + await assetDeliveryProvider.getAssetDeliveryTableListById(requestId: widget.requestId); + } + }); + }); + } else { + // Navigator.of(context).push(MaterialPageRoute( + // builder: (_) => + // DeliveryInspectionFormView( + // deliveryTableModel: model, + // requestModel: dataModel, + // ), + // )); + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => AssetDeliveryStageTabPage( + title: 'Delivery Inspection'.addTranslation, + detailWidget: DeliveryInspectionFormView( + deliveryTableModel: model, + requestModel: dataModel, + ), + tableId: model.id, + ), + )); + } + }, + onViewHistoryTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AssetDeliveryHistoryLogView( + tableItemId: 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 new file mode 100644 index 00000000..e7a41d96 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/asset_delivery_table_card_view.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/controllers/providers/api/device_transfer_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/modules/asset_delivery_module/models/asset_delivery_data_model.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; + +class AssetDeliveryTableCard extends StatelessWidget { + AssetDeliveryTableModel deliveryTableModel; + VoidCallback onCardTap; + VoidCallback onViewHistoryTap; + + AssetDeliveryTableCard({ + super.key, + required this.deliveryTableModel, + required this.onCardTap, + required this.onViewHistoryTap, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 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 ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Delivery Amount: ${deliveryTableModel.deliveryAmount ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Invoice Amount: ${deliveryTableModel.invoiceAmount ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Align( + alignment: Alignment.centerRight, + child: Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14), + ), + ], + ).toShadowContainer(context, borderRadius: 20).onPress(() { + onCardTap(); + }); + } +} diff --git a/lib/modules/asset_delivery_module/pages/asset_delivery_utils.dart b/lib/modules/asset_delivery_module/pages/asset_delivery_utils.dart new file mode 100644 index 00000000..ffa4a1b4 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/asset_delivery_utils.dart @@ -0,0 +1,18 @@ + + +import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_asset_model.dart'; + +class AssetDeliveryUtils{ + ///This is static list for now if change in future backend provide Api as discuss with backend team + static List inspectionChecklist = [ + InspectionChecklistItem(title: 'Physical Inspection As Per Manufacturer Instructions', value: 'flagphysicalInspection'), + InspectionChecklistItem(title: 'Functional Performance As Per Results Of Manufacturer Recommended Check List', value: 'flagFunctionPerformance'), + InspectionChecklistItem(title: 'Grounding Resistance: Ω', value: 'flagGroundingResistance'), + InspectionChecklistItem(title: 'Chassis Leakage Current: µA', value: 'flagChassiss'), + InspectionChecklistItem(title: 'Leads Leakage Current: µA', value: 'flagLeadsLeakage'), + InspectionChecklistItem(title: 'This Device Is Not Subject To Any Alert Or Recall Until The Date Of Preparation Of This Inspection Form', value: 'flagAlert'), + ]; + + + +} \ No newline at end of file diff --git a/lib/modules/asset_delivery_module/pages/attachment_view.dart b/lib/modules/asset_delivery_module/pages/attachment_view.dart new file mode 100644 index 00000000..2f4020e7 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/attachment_view.dart @@ -0,0 +1,152 @@ +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'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/generic_attachment_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.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/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 { + final int? tableItemId; + final int? attachmentType; + bool viewOnly = false; + final List attachmentList; + + AssetDeliveryAttachmentView({ + super.key, + required this.attachmentList, + this.viewOnly=false, + this.attachmentType, + this.tableItemId, + }); + + @override + State createState() => _AssetDeliveryAttachmentViewState(); +} + +class _AssetDeliveryAttachmentViewState extends State { + List _attachments = []; + bool isLoading = false; + late AssetDeliveryProvider assetDeliveryProvider; + + @override + void initState() { + super.initState(); + assetDeliveryProvider = Provider.of(context, listen: false); + WidgetsBinding.instance.addPostFrameCallback((_) { + getInitialData(); + }); + } + + void getInitialData() async { + ///get previoues Attachment from Apis. + // isLoading = true; + setState(() {}); + _attachments = widget.attachmentList; + // await assetDeliveryProvider.getAttachments(tableItemId: widget.tableItemId, attachmentType: widget.attachmentType); + // isLoading = false; + setState(() {}); + } + + @override + Widget build(BuildContext context) { + log('value ${widget.attachmentType}'); + return Scaffold( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + appBar: DefaultAppBar( + title: 'Attachments'.addTranslation, + onBackPress: () { + Navigator.pop(context); + }, + ), + body: widget.attachmentList.isEmpty + ? const NoDataFound().center + : Column( + children: [ + Flexible( + fit: FlexFit.loose, // ✅ expands only as much as needed + child: Column( + children: [ + widget.attachmentType == 5 && !widget.viewOnly + ? AttachmentPicker( + label: context.translation.attachments, + attachment: _attachments, + buttonColor: AppColor.primary10, + onlyImages: false, + showAsListView: true, + buttonIcon: 'quotation_icon'.toSvgAsset(color: AppColor.primary10), + onChange: (attachment) {}, + ).toShadowContainer( + context, + borderRadius: 20, + padding: 12, + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + ) + : FilesList(images: _attachments.map((e) => 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: _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 new file mode 100644 index 00000000..1eb16c9c --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/change_status_bottomsheet.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; + +class AssetDeliveryBottomSheet { + static buildBottomSheetParent({required BuildContext context, required Widget childWidget, bool? isDismissible}) { + return showModalBottomSheet( + context: context, + useSafeArea: true, + isScrollControlled: true, + isDismissible: isDismissible ?? true, + backgroundColor: Colors.transparent, + builder: (context) => SingleChildScrollView( + child: SafeArea(child: childWidget), + ).bottomSheetContainer(context), + ); + } + + static Future changeStatusBottomSheet({required BuildContext context, required VoidCallback cancelStatusTap, required VoidCallback statusChangeTap}) { + return buildBottomSheetParent( + context: context, + childWidget: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox().indicatorWidget(), + 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, + ), + ), + ), + 16.height, + Row( + children: [ + AppFilledButton( + label: 'Cancel'.addTranslation, + maxWidth: true, + buttonColor: Colors.white, + // textColor: context.isDark ? AppColor.neutral30 : Colors.white, + textColor: AppColor.red30, + showBorder: true, + onPressed: () async { + cancelStatusTap(); + }, + ).expanded, + 12.width, + 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 new file mode 100644 index 00000000..2fdfe356 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart @@ -0,0 +1,457 @@ +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/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'; +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/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'; + +class CostCenterFormView extends StatefulWidget { + final List? costCenterList; + final String? itemNo; + final String? itemDescription; + final int? lineId; + final int? tableItemId; + final int? requestId; + final bool? isHmg; + final double? cardPadding; + + const CostCenterFormView({ + super.key, + this.costCenterList, + this.lineId, + this.requestId, + this.tableItemId, + this.itemNo, + this.isHmg, + this.itemDescription, + this.cardPadding, + }); + + @override + State createState() => _CostCenterFormViewState(); +} + +class _CostCenterFormViewState extends State { + CostCenterSerialNoFormModel formModel = CostCenterSerialNoFormModel(costCenters: [], serialNos: []); + late AssetDeliveryProvider assetDeliveryProvider; + CostCenterType _selectedType = CostCenterType.costCenter; + + @override + void initState() { + super.initState(); + assetDeliveryProvider = Provider.of(context, listen: false); + WidgetsBinding.instance.addPostFrameCallback((_) { + getInitialData(); + }); + } + + void getInitialData() async { + formModel = await assetDeliveryProvider.getAssetDeliveryCostCenterListForLines( + lineId: widget.lineId, + tableItemId: widget.tableItemId, + ); + 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) { + await costCenterProvider.getData(id: widget.requestId); + } + } + setState(() {}); + } + + void addItem(List list, T Function() creator) { + setState(() { + list.add(creator()); + }); + } + + void removeItem(List list, int index) { + setState(() { + list.removeAt(index); + }); + } + + void updateItem(List list, int index, void Function(T item) updater) { + setState(() { + updater(list[index]); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + appBar: DefaultAppBar( + title: 'Cost Center for Item'.addTranslation, + // onBackPress: () { + // Navigator.pop(context); + // }, + ), + body: Consumer(builder: (context, provider, child) { + 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 _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; + }); + }, + ), + ), + ], + ), + ); + } + + 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), + ], + ), + ); + } + + 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 { + final CostCenterModel model; + final bool? isHmg; + final int index; + final int? requestId; + final bool isReadOnly; + final void Function(void Function(CostCenterModel model)) onUpdate; + final VoidCallback onRemove; + + const CostCenterItemCard({ + super.key, + required this.model, + this.requestId, + this.isHmg, + required this.index, + required this.isReadOnly, + required this.onUpdate, + required this.onRemove, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + if (!isReadOnly) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + 'Cost Center'.addTranslation.bodyText(context).custom(color: AppColor.textColor(context)), + if (!isReadOnly) + Container( + height: 28, + width: 28, + padding: const EdgeInsets.all(6), + child: "trash".toSvgAsset(height: 20, width: 20), + ).onPress(onRemove), + ], + ), + 8.height, + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + isHmg == true + ? SingleItemDropDownMenu( + context: context, + height: 56.toScreenHeight, + title: 'Cost Center'.addTranslation, + showShadow: false, + backgroundColor: AppColor.fieldBgColor(context), + requestById: requestId, + showAsBottomSheet: true, + matchBy: DropDownMatchType.name, + initialValue: model.costCenter, + onSelect: (status) { + if (status != null) { + model.costCenter = status; + model.costCenterName = status.name; + } + }, + ) + : AppTextFormField( + labelText: 'Cost Center'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.costCenterName ?? '', + textAlign: TextAlign.center, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + onChange: (value) { + model.costCenterName = value; + }, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: 'Quantity'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.quantity != null ? model.quantity.toString() : '', + textAlign: TextAlign.center, + enable: true, + textInputType: TextInputType.number, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + onChange: (value) { + if (value.isNotEmpty) { + model.quantity = double.tryParse(value); + } + }, + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ) + ], + ).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 new file mode 100644 index 00000000..2d113140 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart @@ -0,0 +1,726 @@ +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/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/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/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'; + +class DeliveryInspectionFormView extends StatefulWidget { + static const String id = "/delivery-inspection-form"; + final AssetDeliveryTableModel deliveryTableModel; + final AssetDeliveryDataModel? requestModel; + + DeliveryInspectionFormView({ + Key? key, + required this.deliveryTableModel, + this.requestModel, + }) : super(key: key); + + @override + State createState() => _DeliveryInspectionFormViewState(); +} + +class _DeliveryInspectionFormViewState extends State { + final GlobalKey _formKey = GlobalKey(); + bool viewOnly = false; + AssetDeliveryProvider? assetDeliveryProvider; + DeliveryInspectionFormModel model = DeliveryInspectionFormModel(); + bool isLoading = false; + + @override + void initState() { + viewOnly = widget.deliveryTableModel.status?.value != 2; + WidgetsBinding.instance.addPostFrameCallback((_) { + getInitialData(); + }); + super.initState(); + } + + void getInitialData() async { + assetDeliveryProvider = Provider.of(context, listen: false); + setState(() { + isLoading = true; + }); + final results = await Future.wait([ + assetDeliveryProvider!.getAssetDeliveryInspectionData( + itemId: widget.deliveryTableModel.id, + ), + assetDeliveryProvider!.getDeliveryLinesListById( + itemId: widget.deliveryTableModel.id, + ), + ]); + model = results[0] as DeliveryInspectionFormModel; + setState(() { + isLoading = false; + }); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + // appBar: DefaultAppBar( + // title: 'Delivery Inspection'.addTranslation, + // onBackPress: () { + // Navigator.pop(context); + // }, + // ), + body: isLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Form( + key: _formKey, + child: Column( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!viewOnly) ...[inspectionDetailsForm()] else ...[inspectionDetails()], + 8.height, + Text( + 'Total Time'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + 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( + "Lines", + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + linesList(context), + ], + ).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: () { + 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, + ], + ), + ], + ) + : 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) { + return Consumer(builder: (context, provider, child) { + List linesList = provider.deliveryLinesList; + return provider.isDeliveryLineListLoading + ? SizedBox.expand(child: const CircularProgressIndicator(color: AppColor.primary10).center) + : linesList.isEmpty + ? const NoDataFound().center + : ListView.separated( + itemCount: linesList.length, + separatorBuilder: (_, __) => 8.height, + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (listContext, itemIndex) { + DeliveryLineModel model = linesList[itemIndex]; + return AssetDeliveryLineCard( + deliveryLineModel: model, + isViewOnly: viewOnly, + editPress: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CostCenterFormView( + itemNo: model.itemNumber, + lineId: model.id, + isHmg: widget.requestModel?.isHMG, + tableItemId: widget.deliveryTableModel.id, + requestId: widget.requestModel?.id, + itemDescription: model.itemDescription, + ))); + }, + ); + }, + ); + }); + } + + void calculateTotalTime() { + final from = model.deliveryInspectionInspectedDateFrom; + final to = model.deliveryInspectionInspectedDateTo; + + if (from == null || to == null) { + model.totalTime = null; + return; + } + + 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: [ + _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, + _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); + } + }, + ), + ], + + // 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, + hideShadow: true, + height: 56.toScreenHeight, + backgroundColor: AppColor.fieldBgColor(context), + date: model.deliveryInspectionInspectedDateFrom, + from: DateTime(DateTime.now().year, 1, 1), + formatDateWithTime: true, + onDatePicker: (selectedDate) async { + final selectedTime = await showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + 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, + hideShadow: true, + height: 56.toScreenHeight, + backgroundColor: AppColor.fieldBgColor(context), + date: model.deliveryInspectionInspectedDateTo, + from: model.deliveryInspectionInspectedDateFrom, + formatDateWithTime: true, + onDatePicker: (selectedDate) async { + if (model.deliveryInspectionInspectedDateFrom == null) { + "Please select time to first".showToast; + return; + } + + final selectedTime = await showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + 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, + children: [ + Text( + 'Inspected by'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + model.deliveryInspectionInspectedBy ?? '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + Text( + 'Approved by'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + model.deliveryInspectionApprovedBy ?? '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + Text( + 'Inspected Date/Time From'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + model.deliveryInspectionInspectedDateFrom != null ? model.deliveryInspectionInspectedDateFrom!.toString().toServiceRequestDetailsFormat : '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + Text( + 'Inspected Date/Time To'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + model.deliveryInspectionInspectedDateTo != null ? model.deliveryInspectionInspectedDateTo!.toString().toServiceRequestDetailsFormat : '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + ], + ); + } + + 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 { + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await assetDeliveryProvider!.changeDeliveryStatusToCancel(itemId: widget.deliveryTableModel.id).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context); + await assetDeliveryProvider!.getAssetDeliveryTableListById(requestId: widget.requestModel?.id); + } + }); + } + + 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) => AssetDeliveryStageTabPage( + title: 'Technical Inspection'.addTranslation, + detailWidget: TechnicalInspectionLinesListView( + deliveryTableModel: widget.deliveryTableModel, + requestModel: widget.requestModel, + ), + tableId: widget.deliveryTableModel.id, + ))); + } else { + Navigator.push( + context, + MaterialPageRoute( + 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, + // ))); + } + } + + void _changeStatusTap() async { + _formKey.currentState!.save(); + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + model.assetDeliveryExternalDeliveryId = widget.deliveryTableModel.id; + await assetDeliveryProvider!.changeDeliveryStatus(requestPayload: model.toJson()).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context); + await assetDeliveryProvider!.getAssetDeliveryTableListById(requestId: widget.requestModel?.id); + } + }); + } + + 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 new file mode 100644 index 00000000..9c18de9b --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/delivery_inspection/oracle_user_autocomplete_field.dart @@ -0,0 +1,117 @@ +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/text_extensions.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/oracle_user_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/app_style/app_text_style.dart'; +import 'package:test_sa/views/app_style/sizing.dart'; + +class OracleUserAutoCompleteField extends StatefulWidget { + final String initialValue; + final String label; + final bool clearAfterPick; + final Function(OracleUserModel) onPick; + final Function(String)? onChanged; + + const OracleUserAutoCompleteField({Key? key, required this.initialValue, required this.label, required this.onPick, this.clearAfterPick = true, this.onChanged}) : super(key: key); + + @override + _AutoCompletePartsFieldState createState() => _AutoCompletePartsFieldState(); +} + +class _AutoCompletePartsFieldState extends State { + AssetDeliveryProvider? assetDeliveryProvider; + + // late TextEditingController _controller; + + @override + void initState() { + // _controller = TextEditingController(text: widget.initialValue); + super.initState(); + } + + @override + void didUpdateWidget(covariant OracleUserAutoCompleteField oldWidget) { + // if (widget.initialValue != oldWidget.initialValue) { + // _controller = TextEditingController(text: widget.initialValue); + // } + super.didUpdateWidget(oldWidget); + } + + @override + void dispose() { + // _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + assetDeliveryProvider ??= Provider.of(context); + final border = UnderlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(10)); + return Container( + decoration: BoxDecoration( + color: AppColor.background(context), + borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)), + // 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: fieldTextEditingController, + focusNode: fieldFocusNode, + style: AppTextStyles.bodyText.copyWith(color: AppColor.black10), + textAlign: TextAlign.start, + decoration: InputDecoration( + border: border, + disabledBorder: border, + focusedBorder: border, + enabledBorder: border, + errorBorder: border, + contentPadding: EdgeInsets.symmetric(vertical: 8.toScreenHeight, horizontal: 16.toScreenWidth), + constraints: const BoxConstraints(), + suffixIconConstraints: const BoxConstraints(minWidth: 0), + filled: true, + fillColor: AppColor.fieldBgColor(context), + errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60), + floatingLabelStyle: AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? null : AppColor.neutral20), + labelText: widget.label, + labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.textColor(context)), + ), + textInputAction: TextInputAction.search, + onChanged: (text) { + if (widget.onChanged != null) { + widget.onChanged!(text); + fieldTextEditingController.text = text; + } + }, + onSubmitted: (String value) { + onFieldSubmitted(); + }, + ); + }, + onSelected: (OracleUserModel selection) { + // 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/cost_center_card_view.dart b/lib/modules/asset_delivery_module/pages/end_user_acceptance/cost_center_card_view.dart new file mode 100644 index 00000000..59237ffd --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/cost_center_card_view.dart @@ -0,0 +1,64 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:test_sa/common_widgets/lookup_autocomplete_field.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/modules/asset_delivery_module/models/cost_center_model.dart'; +import 'package:test_sa/modules/asset_inventory_module/models/asset_inventory_model.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; + +class EndUserCostCenterCardView extends StatelessWidget { + CostCenterModel costModel; + VoidCallback? editPress; + bool isViewOnly; + + EndUserCostCenterCardView({ + super.key, + required this.costModel, + this.editPress, + required this.isViewOnly, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + //need to check this @waseem. + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + 'Cost Center Name: ${costModel.costCenterName ?? '-'}', + overflow: TextOverflow.ellipsis, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + ), + 8.width, + if (!isViewOnly) + 'edit_icon'.toSvgAsset(height: 18, width: 18).onPress(() { + if (editPress != null) { + editPress!(); + } + }), + ], + ), + Text( + 'Accepted By: ${costModel.acceptedBy ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ),Text( + 'Accepted By Email: ${costModel.acceptedByEmail ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ),Text( + 'Acceptance Date: ${costModel.acceptanceDate!=null? costModel.acceptanceDate.toString().toAssetDetailsFormat: '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12); + } +} 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 new file mode 100644 index 00000000..f023c938 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_asset_detail_card.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/end_user_asset_details_model.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; + +class EndUserAssetDetailCard extends StatelessWidget { + EndUserAssetDetailsModel assetModel; + bool isViewOnly; + VoidCallback? editPress; + + EndUserAssetDetailCard({ + super.key, + required this.isViewOnly, + required this.assetModel, + this.editPress, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + assetModel.status != null + ? StatusLabel( + label: assetModel.status?.name, + radius: 4, + textColor: getAssetStatusColor(context: context, status: assetModel.status?.name ?? '', isBackground: false), + backgroundColor: getAssetStatusColor(context: context, status: assetModel.status?.name ?? ''), + ) + : const SizedBox(), + if (!isViewOnly) + 'edit_icon'.toSvgAsset().onPress(() { + if (editPress != null) { + editPress!(); + } + }), + ], + ), + 8.height, + if (assetModel.type == 'Accessory') ...[ + Text( + 'Type: ${assetModel.type ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Item Number: ${assetModel.itemNumber ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Received Quantity: ${assetModel.receivedQty ?? '-'}', + 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), + ), + Text( + 'Rejected Quantity: ${assetModel.rejectedQty ?? '-'}', + 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), + ), + ] else ...[ + Text( + 'Type: ${assetModel.type ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Item Number: ${assetModel.itemNumber ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Asset Number: ${assetModel.assetNumber ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Serial No: ${assetModel.serialNo ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Replaced Asset No: ${assetModel.replacedAssetNo ?? '-'}', + 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), + ), + ], + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12); + } + + Color getAssetStatusColor({required BuildContext context, required String status, bool? isBackground = true}) { + switch (status) { + case 'Accept': + return isBackground! ? AppColor.green70.withOpacity(0.4) : AppColor.green15; + case 'Reject': + return isBackground! ? AppColor.red30.withOpacity(0.4) : AppColor.red30; + default: + return Colors.blueGrey; + } + } +} 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 new file mode 100644 index 00000000..f032d75a --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart @@ -0,0 +1,198 @@ +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/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'; +import 'package:test_sa/modules/asset_delivery_module/pages/end_user_acceptance/cost_center_card_view.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_cost_center_details_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_lazy_loading.dart'; +import '../../../../new_views/common_widgets/app_filled_button.dart'; +import '../../../../new_views/common_widgets/default_app_bar.dart'; + +class EndUserCostCenterLIstView extends StatefulWidget { + final AssetDeliveryTableModel deliveryTableModel; + final AssetDeliveryDataModel? requestModel; + + EndUserCostCenterLIstView({ + Key? key, + required this.deliveryTableModel, + this.requestModel, + }) : super(key: key); + + @override + State createState() => _EndUserCostCenterLIstViewState(); +} + +class _EndUserCostCenterLIstViewState extends State { + final GlobalKey _formKey = GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); + bool viewOnly = false; + List costCenterList = []; + AssetDeliveryProvider? assetDeliveryProvider; + bool isLoading = false; + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + getInitialData(); + }); + super.initState(); + } + + void getInitialData() async { + assetDeliveryProvider = Provider.of(context, listen: false); + isLoading = true; + if (widget.deliveryTableModel.status?.value != null && widget.deliveryTableModel.status!.value! > 4) { + viewOnly = true; + } + setState(() {}); + costCenterList = (await assetDeliveryProvider?.getEndUserCostCenterList( + tableItemId: widget.deliveryTableModel.id, + )) ?? + []; + isLoading = false; + setState(() {}); + } + + @override + void dispose() { + super.dispose(); + } + + @override + 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), + // ), + body: Form( + key: _formKey, + child: isLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Column( + children: [ + //Refactor this + SingleChildScrollView(padding: const EdgeInsets.all(16), child: linesList(context).toShadowContainer(context, borderRadius: 20, padding: 12)).expanded, + + !viewOnly + ? FooterActionButton.footerContainer( + context: context, + child: 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: AppColor.red30, + showBorder: true, + onPressed: _cancelTap, + ).expanded, + 12.width, + AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded, + ], + ), + ], + )) + : 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); + }), + ), + ], + ), + ), + ); + } + + //Need to refector don't use listview and singlechildview both @waseem. + Widget linesList(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Cost Center", + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + ListView.separated( + itemCount: costCenterList.length, + separatorBuilder: (_, __) => 8.height, + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (listContext, itemIndex) { + return EndUserCostCenterCardView( + costModel: costCenterList[itemIndex], + isViewOnly: viewOnly, + editPress: () async { + bool isRefresh = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UpdateEndUserCostCenterDetailsView( + costCenterModel: costCenterList[itemIndex], + tableItemId: widget.deliveryTableModel.id, + isHmg: widget.requestModel?.isHMG, + ))); + if (isRefresh) { + getInitialData(); + } + }, + ); + }, + ), + ], + ); + } + + void _attachmentTap() async { + HelperFunction.attachmentTap(context: context, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id); + } + + void _cancelTap() async { + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await assetDeliveryProvider?.changeDeliveryStatusToCancel(itemId: widget.deliveryTableModel.id).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context); + await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestModel?.id); + } + }); + } + + void _changeStatusTap() async { + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await assetDeliveryProvider?.changeDeliveryStatus(itemId: widget.deliveryTableModel.id).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context); + await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestModel?.id); + } + }); + } + + void _nextTap() async { + // Navigator.push(context, MaterialPageRoute(builder: (context) => EndUserCostCenterLIstView())); + } +} 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 new file mode 100644 index 00000000..834b96b3 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_asset_details_view.dart @@ -0,0 +1,520 @@ +// 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/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 { + final EndUserAssetDetailsModel? assetDetailsModel; + final String? costCenterName; + final int? tableId; + + const UpdateEndUserAssetDetailsView({ + Key? key, + this.assetDetailsModel, + this.costCenterName, + this.tableId, + }) : super(key: key); + + @override + State createState() => _UpdateEndUserAssetDetailsViewState(); +} + +class _UpdateEndUserAssetDetailsViewState extends State { + final GlobalKey _formKey = GlobalKey(); + + late AssetDeliveryProvider assetDeliveryProvider; + EndUserAssetDetailsModel model = EndUserAssetDetailsModel(); + + late TextEditingController quantityController; + late TextEditingController descriptionController; + + @override + void initState() { + super.initState(); + quantityController = TextEditingController(); + descriptionController = TextEditingController(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + _loadInitialData(); + }); + } + + void _loadInitialData() { + 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; + 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( + 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: _formBody(context), + ).expanded, + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + buttonColor: AppColor.primary10, + label: 'Save'.addTranslation, + maxWidth: true, + onPressed: _saveTap, + ), + ), + ], + ), + ), + ); + } + + 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(); + 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 new file mode 100644 index 00000000..1234a018 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_cost_center_details_view.dart @@ -0,0 +1,331 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.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/modules/asset_delivery_module/models/cost_center_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/end_user_asset_details_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/end_user_acceptance/end_user_asset_detail_card.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/end_user_acceptance/update_end_user_asset_details_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_lazy_loading.dart'; +import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; +import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; +import 'package:test_sa/views/widgets/e_signature/e_signature.dart'; +import '../../../../new_views/common_widgets/app_filled_button.dart'; +import '../../../../new_views/common_widgets/default_app_bar.dart'; + +class UpdateEndUserCostCenterDetailsView extends StatefulWidget { + int? tableItemId; + CostCenterModel? costCenterModel; + bool? isHmg; + + UpdateEndUserCostCenterDetailsView({ + Key? key, + this.tableItemId, + this.costCenterModel, + this.isHmg, + }) : super(key: key); + + @override + State createState() => _UpdateEndUserCostCenterDetailsViewState(); +} + +class _UpdateEndUserCostCenterDetailsViewState extends State { + final GlobalKey _formKey = GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); + bool viewOnly = false; + CostCenterModel model = CostCenterModel(); + List assetList = []; + late AssetDeliveryProvider assetDeliveryProvider; + Uint8List? newSignature; + String? endUserSignature; + bool isLoading = false; + late TextEditingController acceptedByController; + late TextEditingController acceptedByEmailController; + + @override + void initState() { + acceptedByController = TextEditingController(); + acceptedByEmailController = TextEditingController(); + WidgetsBinding.instance.addPostFrameCallback((_) { + getInitialData(); + }); + super.initState(); + } + + void getInitialData() async { + assetDeliveryProvider = Provider.of(context, listen: false); + + if (widget.costCenterModel != null) { + model = CostCenterModel.fromJson(widget.costCenterModel!.toEndUserAcceptanceFormJson()); + _fillControllers(); + } + setState(() { + isLoading = true; + }); + assetList = await assetDeliveryProvider.getEndUserAssetList(tableItemId: widget.tableItemId, costCenterName: widget.costCenterModel?.costCenterName); + setState(() { + isLoading = false; + }); + } + + void _fillControllers() { + acceptedByController.text = model.acceptedBy ?? ''; + acceptedByEmailController.text = model.acceptedByEmail ?? ''; + } + + @override + void dispose() { + acceptedByController.dispose(); + acceptedByEmailController.dispose(); + super.dispose(); + } + + @override + 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), + ), + body: Form( + key: _formKey, + child: Column( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + inspectionDetailsForm(), + 8.height, + const Divider().defaultStyle(context), + + ///Need to implement later as discuss with Ahmed. + // Text( + // "Acceptance Form", + // style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + // ), + // 6.height, + // Row( + // children: [ + // 'warning_icon'.toSvgAsset(height: 16, width: 16), + // 6.width, + // Text( + // 'Review the acceptance form'.addTranslation, + // style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + // ), + // 6.width, + // 'web_link_icon'.toSvgAsset(height: 20, width: 20), + // ], + // ), + 16.height, + Text( + "Items", + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + endUserAssetList(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 endUserAssetList(BuildContext context) { + return isLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : ListView.separated( + itemCount: assetList.length, + separatorBuilder: (_, __) => 8.height, + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (listContext, itemIndex) { + EndUserAssetDetailsModel model = assetList[itemIndex]; + return EndUserAssetDetailCard( + assetModel: model, + isViewOnly: viewOnly, + editPress: () async { + bool isRefresh = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UpdateEndUserAssetDetailsView( + assetDetailsModel: model, + costCenterName: widget.costCenterModel?.costCenterName, + tableId: widget.tableItemId, + ))); + if (isRefresh) { + getInitialData(); + } + }, + ); + }, + ); + } + + Widget inspectionDetailsForm() { + return Column( + children: [ + ADatePicker( + label: 'Accepted Date'.addTranslation, + hideShadow: true, + height: 56.toScreenHeight, + backgroundColor: AppColor.fieldBgColor(context), + date: model.acceptanceDate, + from: DateTime(DateTime.now().year, 1, 1), + 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.acceptanceDate = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute); + setState(() {}); + } + }); + }, + ), + 8.height, + if (widget.isHmg == true) ...[ + OracleUserAutoCompleteField( + clearAfterPick: false, + initialValue: model.acceptedBy ?? '', + label: 'Accepted By'.addTranslation, + onPick: (value) { + model.acceptedBy = value.fullName; + model.acceptedByEmail = value.employeeEmail; + acceptedByEmailController.text = value.employeeEmail ?? ''; + setState(() {}); + }, + onChanged: (value) {}, + ), + 8.height, + ] else ...[ + AppTextFormField( + labelText: 'Accepted By'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + controller: acceptedByController, + textAlign: TextAlign.center, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + onChange: (value) { + model.acceptedBy = value; + }, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + ], + AppTextFormField( + labelText: 'Accepted By Email'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + controller: acceptedByEmailController, + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + onChange: (value) { + model.acceptedByEmail = value; + }, + textAlign: TextAlign.center, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + enable: widget.isHmg == true ? false : true, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + ESignature( + title: 'End User Signature', + oldSignature: model.acceptedBySignature, + newSignature: newSignature, + backgroundColor: AppColor.neutral100, + showShadow: false, + onChange: (signature) { + updateSignature(signature: signature); + }, + ) + ], + ); + } + + void updateSignature({Uint8List? signature}) { + if (signature == null || signature.isEmpty) { + setState(() { + newSignature = null; + model.acceptedBySignature = null; + }); + return; + } + setState(() { + newSignature = signature; + model.acceptedBySignature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}"; + }); + } + + void _saveTap() async { + if (!_formKey.currentState!.validate()) { + return; + } + if (model.acceptanceDate == null) { + "Select Accepted date".showToast; + return; + } + if (widget.isHmg == true) { + if ((model.acceptedBy ?? '').isEmpty || (model.acceptedByEmail ?? '').isEmpty) { + 'Accepted By and Email are required'.addTranslation.showToast; + return; + } + } + if (model.acceptedBySignature == null || model.acceptedBySignature!.isEmpty) { + 'Signature is required'.addTranslation.showToast; + return; + } + _formKey.currentState!.save(); + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + model.assetDeliveryExternalDetailId = widget.tableItemId; + await assetDeliveryProvider.saveEndUserFormData(model: model).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context, true); + } + }); + } +} diff --git a/lib/modules/asset_delivery_module/pages/history_log_view.dart b/lib/modules/asset_delivery_module/pages/history_log_view.dart new file mode 100644 index 00000000..a5f6bf04 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/history_log_view.dart @@ -0,0 +1,168 @@ +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/modules/asset_delivery_module/models/history_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart'; +import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; + +class AssetDeliveryHistoryLogView extends StatefulWidget { + final int? tableItemId; + const AssetDeliveryHistoryLogView({Key? key,this.tableItemId}) : super(key: key); + + @override + State createState() => _AssetDeliveryHistoryLogViewState(); +} + + + + +class _AssetDeliveryHistoryLogViewState extends State { + List history = []; + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + getInitialData(); + }); + super.initState(); + } + + + void getInitialData() async { + history = await Provider.of(context, listen: false).getHistory(tableItemId: widget.tableItemId); + } + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + // appBar: DefaultAppBar( + // title: 'History Log'.addTranslation, + // onBackPress: () { + // Navigator.pop(context); + // }, + // ), + body: Consumer(builder: (context, provider, _) { + return provider.isLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Stack( + children: [ + Padding( + padding: EdgeInsets.only(left: 122.toScreenWidth), + child: const VerticalDivider( + color: AppColor.white40, + thickness: 2, + width: 20, + indent: 10, + endIndent: 10, + ), + ), + SafeArea( + child: Builder(builder: (cxt) { + List items = []; + String lastStatus = ""; + DateTime? nextDate; + for (int i = 0; i < history.length; i++) { + var object = history[i]; + String currentStatus = object.status!.name!; + if (lastStatus.isEmpty) { + lastStatus = currentStatus; + } + if (lastStatus == currentStatus) { + if (i + 1 < history.length) { + nextDate = history[i + 1].createdDate; + String timeDifference = CMRequestUtils.calculateTimeDifference(object.createdDate!, nextDate!); + object.timeDifference = timeDifference; + } else { + object.timeDifference = ''; // Empty string for the last action + } + items.add(historyLogCard(cxt, object)); + } else { + items.add(statusCard(cxt, lastStatus)); + lastStatus = currentStatus; + if (i + 1 < history.length) { + nextDate = history[i + 1].createdDate; + String timeDifference = CMRequestUtils.calculateTimeDifference(object.createdDate!, nextDate!); + object.timeDifference = timeDifference; + } else { + object.timeDifference = ''; // Empty string for the last action + } + items.add(historyLogCard(cxt, object)); + } + } + items.add(statusCard(cxt, lastStatus)); + return SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column(children: items), + ); + }), + ), + ], + ); + }), + );} + + Widget historyLogCard(BuildContext context, AssetDeliveryHistoryModel object) { + String title = object.status!.name!; + + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Align( + alignment: Alignment.centerLeft, + child: Text( + object.createdDate.toString().toServiceRequestCardFormat, + textAlign: TextAlign.end, + style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + ).expanded, + Expanded( + flex: 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title.bodyText(context).custom(color: AppColor.textColor(context)), + // object.timeDifference.isNotEmpty ? object.timeDifference?.tinyFont(context).custom(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120) : const SizedBox(), + ], + ), + ), + ], + ).toShadowContainer(context, padding: 16).paddingOnly(bottom: 16); + } + + Widget statusCard(BuildContext context, String status) { + return SizedBox( + width: 153.toScreenWidth, + child: StatusLabel( + radius: 4, + label: statusText(status.toLowerCase()), + labelInCenter: true, + textColor: AppColor.neutral50, + backgroundColor: AppColor.orange30, + ), + ).paddingOnly(bottom: 16, start: 40); + } +// Need to check status text we have + String statusText(String status) { + if (status == "new" || status == "open") { + return "New".addTranslation; + } else if (status == "in progress") { + return "Status update to In Progress".addTranslation; + } else if (status == "closed") { + return "Status update to Closed".addTranslation; + } else if (status == "completed") { + return "Status update to Completed".addTranslation; + } else if (status == "fixed") { + return "Status update to Fixed".addTranslation; + } else if (status == "reject") { + return "Status update to Rejected".addTranslation; + } else + return status; + } +} diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/search_asset_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/search_asset_view.dart new file mode 100644 index 00000000..4344c633 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/search_asset_view.dart @@ -0,0 +1,194 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/devices_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/widget_extensions.dart'; +import 'package:test_sa/models/device/asset.dart'; +import 'package:test_sa/models/device/asset_search.dart'; +import 'package:test_sa/models/device/model_definition.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/technical_inpection_asset_model.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/custom_app_bar.dart'; +import 'package:test_sa/views/widgets/equipment/asset_item_listview.dart'; +import 'package:test_sa/views/widgets/horizontal_list_widget.dart'; +import 'package:test_sa/views/widgets/loaders/lazy_loading.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; + +class TechnicalInspectionSearchAssetView extends StatefulWidget { + /// add on route + static const String id = "search_asset_view"; + final int? lineId; + + const TechnicalInspectionSearchAssetView({ + Key? key, + this.lineId, + }) : super(key: key); + + @override + State createState() => _TechnicalInspectionSearchAssetViewState(); +} + +class _TechnicalInspectionSearchAssetViewState extends State { + int _selectedIndex = 0; + DeviceSearch? search; + late TextEditingController _searchController; + late AssetProvider _deviceProvider; + + //check for this list if not need remove + final List _searchableList = []; + final GlobalKey _formKey = GlobalKey(); + bool _isFirst = true; + + @override + void initState() { + _searchController = TextEditingController(); + super.initState(); + } + + @override + void dispose() { + _searchController.dispose(); + _deviceProvider.searchReset(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + List searchBy = [ + context.translation.assetName, + context.translation.assetNumber, + 'model'.addTranslation, + 'manufacturer'.addTranslation, + // 'assetType'.addTranslation, + // 'assetOrigin'.addTranslation, + ]; + + _deviceProvider = Provider.of(context, listen: false); + + return Scaffold( + resizeToAvoidBottomInset: false, + appBar: CustomAppBar( + title: context.translation.searchAsset, + ), + body: Column( + children: [ + HorizontalListWidget( + list: searchBy, + callBackFunction: (index) { + setState(() { + _selectedIndex = index; + }); + }, + ).paddingOnly(top: 16, bottom: 0), + Form( + key: _formKey, + child: AppTextFormField( + controller: _searchController, + textInputAction: TextInputAction.search, + labelText: "${context.translation.searchBy} ${searchBy[_selectedIndex]}", + onAction: _search, + onChange: (text) { + _searchController.text = text; + _searchController.selection = TextSelection.fromPosition(TextPosition(offset: _searchController.text.length)); + setState(() {}); + }, + onSaved: (value) { + setState(() { + search = DeviceSearch(); + }); + _setValue(value); + }, + suffixIcon: IconButton( + icon: const Icon(Icons.search), + splashColor: Colors.transparent, + onPressed: _searchController.text.isNotEmpty ? _search : null, + color: AppColor.neutral20, + ).paddingOnly(end: 0), + ).paddingOnly(top: 16, start: 16, end: 16, bottom: 8)), + Expanded( + child: _searchableList.isEmpty + ? _isFirst + ? const SizedBox() + : NoDataFound(message: context.translation.noDeviceFound) + : LazyLoading( + nextPage: _deviceProvider.nextPage, + onLazyLoad: () async { + if (_searchController.text.isNotEmpty) { + await _deviceProvider.getAssetsForAssetDelivery(search: search, lineId: widget.lineId, isSearchBy: true); + setState(() { + _searchableList.clear(); + _searchableList.addAll(_deviceProvider.technicalInspectionAsset); + }); + } + }, + child: ListView.separated( + itemCount: _searchableList.length, + separatorBuilder: (listContext, itemIndex) => 8.height, + padding: const EdgeInsets.all(16), + itemBuilder: (listContext, itemIndex) { + return AssetItemListView( + device: Asset( + id: _searchableList[itemIndex].id, + assetNumber: _searchableList[itemIndex].assetNumber, + assetSerialNo: _searchableList[itemIndex].assetNumber, + modelDefinition: ModelDefinition(assetName: _searchableList[itemIndex].assetName, modelName: _searchableList[itemIndex].model), + ), + onPressed: (device) { + Navigator.of(context).pop(device); + }, + ); + }, + ), + ), + ) + ], + )); + } + + void _search({bool loadMore = false}) async { + FocusScope.of(context).unfocus(); + _formKey.currentState?.save(); + _searchableList.clear(); + _deviceProvider.technicalInspectionAsset.clear(); + setState(() {}); + _deviceProvider.searchReset(); + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await _deviceProvider.getAssetsForAssetDelivery( + search: search, + lineId: widget.lineId, + isSearchBy: true, + ); + + setState(() { + _searchableList.clear(); + _searchableList.addAll(_deviceProvider.technicalInspectionAsset); + _isFirst = false; + }); + + Navigator.pop(context); + } + + _setValue(String? value) { + search ??= DeviceSearch(); + switch (_selectedIndex) { + case 0: + search!.assetName = value; + break; + case 1: + search!.assetNo = value; + break; + case 2: + search!.model = value; + break; + case 3: + search!.manufacturer = value; + break; + } + } +} 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 new file mode 100644 index 00000000..e98c1213 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/status_checklist_view.dart @@ -0,0 +1,215 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/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/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 InspectionCheckListView extends StatefulWidget { + final List? initialValues; + + const InspectionCheckListView({super.key, this.initialValues}); + + @override + State createState() => _InspectionCheckListViewState(); +} + +class _InspectionCheckListViewState extends State { + late List checklist; + + @override + void initState() { + super.initState(); + checklist = AssetDeliveryUtils.inspectionChecklist.map((e) => InspectionChecklistItem(title: e.title)).toList(); + + if (widget.initialValues != null && widget.initialValues!.isNotEmpty) { + for (final item in checklist) { + final matched = widget.initialValues!.firstWhere( + (e) => e.title == item.title, + orElse: () => InspectionChecklistItem(), + ); + item.status = matched.status; // null-safe + } + } + } + + void updateStatus(int index, String value) { + setState(() { + checklist[index].status = value; + }); + } + + List get selectedValues => checklist.where((e) => e.status != null).toList(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: DefaultAppBar( + title: 'Asset Check List'.addTranslation, + ), + body: Column( + children: [ + 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( + buttonColor: AppColor.primary10, + label: 'Save'.addTranslation, + maxWidth: true, + onPressed: () { + Navigator.pop(context, selectedValues); + }, + ), + ), + ], + ), + ); + } + + 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 new file mode 100644 index 00000000..b09eb878 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_asset_card_view.dart @@ -0,0 +1,138 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/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/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; + +class TechnicalInspectionAssetCard extends StatelessWidget { + TechnicalInspectionAssetModel assetModel; + bool isViewOnly; + bool ischild; + VoidCallback? editPress; + + TechnicalInspectionAssetCard({ + super.key, + required this.isViewOnly, + this.ischild = false, + required this.assetModel, + this.editPress, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!ischild) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + StatusLabel( + label: getAssetStatus(), + radius: 4, + textColor: getAssetStatusColor(context: context, status: getAssetStatus(), isBackground: false), + backgroundColor: getAssetStatusColor(context: context, status: getAssetStatus()), + ), + if (!isViewOnly) + 'edit_icon'.toSvgAsset().onPress(() { + if (editPress != null) { + editPress!(); + } + }), + ], + ), + 8.height, + ], + if (ischild) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Asset Number: ${assetModel.assetNumber ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + if (!isViewOnly) + 'edit_icon'.toSvgAsset().onPress(() { + if (editPress != null) { + editPress!(); + } + }), + ], + ), + 2.height, + ], + Text( + 'Asset Name: ${assetModel.modelDefinition?.assetName ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + if (!ischild) + Text( + '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), + ), + Text( + 'System ID: ${assetModel.systemId ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Medical Department: ${assetModel.department?.name ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Installation Date: ${assetModel.installationDate ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Asset Type: ${assetModel.assetType?.name ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12); + } + + Color getAssetStatusColor({required BuildContext context, required String status, bool? isBackground = true}) { + switch (status) { + case 'PASS': + return isBackground! ? AppColor.green70.withOpacity(0.4) : AppColor.green15; + case 'FAIL': + return isBackground! ? AppColor.red30.withOpacity(0.4) : AppColor.red30; + default: + return Colors.white10; + } + } + + String getAssetStatus() { + final flags = [ + assetModel.flagphysicalInspection, + assetModel.flagFunctionPerformance, + assetModel.flagGroundingResistance, + assetModel.flagChassiss, + assetModel.flagLeadsLeakage, + assetModel.flagAlert, + ]; + if (flags.any((f) => f == false)) { + return 'FAIL'; + } + if (flags.every((f) => f == true)) { + return 'PASS'; + } + return 'FAIL'; + } +} diff --git a/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_cost_center_card_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_cost_center_card_view.dart new file mode 100644 index 00000000..c9488779 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_cost_center_card_view.dart @@ -0,0 +1,58 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:test_sa/common_widgets/lookup_autocomplete_field.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/modules/asset_delivery_module/models/cost_center_model.dart'; +import 'package:test_sa/modules/asset_inventory_module/models/asset_inventory_model.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; + +class TechnicalInspectionCostCard extends StatelessWidget { + CostCenterModel costModel; + VoidCallback? editPress; + bool isViewOnly; + + TechnicalInspectionCostCard({ + super.key, + required this.costModel, + this.editPress, + required this.isViewOnly, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + //need to check this @waseem. + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + 'Cost Center Name: ${costModel.costCenterName ?? '-'}', + overflow: TextOverflow.ellipsis, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + ), + 8.width, + if (!isViewOnly) + 'edit_icon'.toSvgAsset(height: 18, width: 18).onPress(() { + if (editPress != null) { + editPress!(); + } + }), + ], + ), + Text( + 'Quantity: ${costModel.quantity ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12); + } +} 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 new file mode 100644 index 00000000..e2c05093 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart @@ -0,0 +1,284 @@ +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/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'; +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_lazy_loading.dart'; +import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; +import 'package:test_sa/providers/service_request_providers/last_situation_provider.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'; +import '../../../../new_views/common_widgets/default_app_bar.dart'; + +class TechnicalInspectionLinesListView extends StatefulWidget { + final AssetDeliveryTableModel deliveryTableModel; + final AssetDeliveryDataModel? requestModel; + + TechnicalInspectionLinesListView({ + Key? key, + required this.deliveryTableModel, + this.requestModel, + }) : super(key: key); + + @override + State createState() => _TechnicalInspectionLinesListViewState(); +} + +class _TechnicalInspectionLinesListViewState extends State { + final GlobalKey _formKey = GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); + bool viewOnly = false; + bool isLoading = false; + AssetDeliveryProvider? assetDeliveryProvider; + List technicalInspectionLinesList = []; + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + getInitialData(); + }); + super.initState(); + } + + void getInitialData() async { + assetDeliveryProvider = Provider.of(context, listen: false); + viewOnly = widget.deliveryTableModel.status?.value != 3; + isLoading = true; + setState(() {}); + technicalInspectionLinesList = await assetDeliveryProvider!.getTechnicalInspectionLinesListById( + //TODO uncommit this after testing @waseem + itemId: widget.deliveryTableModel.id, + // itemId: 2, + ); + isLoading = false; + setState(() {}); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + // appBar: DefaultAppBar( + // title: 'Technical Inspection'.addTranslation, + // titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + // ), + body: Form( + key: _formKey, + child: (isLoading) + ? const CircularProgressIndicator(color: AppColor.primary10).center + : (technicalInspectionLinesList.isEmpty) + ? const NoDataFound().center + : Column( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + ListView.separated( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + 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), + ], + ), + ).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, + ], + ), + ), + ], + ), + ), + ); + } + +//May be need to show data. + Widget inspectionDetails() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Inspected by'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + 'Mahmoud Shrouf'.addTranslation, + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + Text( + 'Approved by'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + 'Mahmoud Shrouf'.addTranslation, + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + Text( + 'Inspected Date/Time From'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + '19 August, 2025'.addTranslation, + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + 8.height, + Text( + 'Inspected Date/Time To'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + Text( + '19 August, 2025'.addTranslation, + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + ], + ); + } + + void _changeStatusTap() async { + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await assetDeliveryProvider?.changeDeliveryStatus(itemId: widget.deliveryTableModel.id).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context); + await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestModel?.id); + } + }); + } + + void _attachmentTap() async { + HelperFunction.attachmentTap(context: context, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id); + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => AssetDeliveryAttachmentView( + // tableItemId: widget.deliveryTableModel.id, + // ))); + } + + void _cancelTap() async { + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await assetDeliveryProvider?.changeDeliveryStatusToCancel(itemId: widget.deliveryTableModel.id).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context); + await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestModel?.id); + } + }); + } + + void _nextTap() async { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => EndUserCostCenterLIstView( + // requestModel: widget.requestModel, + // deliveryTableModel: widget.deliveryTableModel, + // ))); + + Navigator.push( + context, + MaterialPageRoute( + 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/technical_inspection_parent_asset_list_view.dart b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_parent_asset_list_view.dart new file mode 100644 index 00000000..5f6a60c6 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_parent_asset_list_view.dart @@ -0,0 +1,133 @@ +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/modules/asset_delivery_module/models/technical_inspection_asset_model.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_parent_asset_view.dart'; +import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; +import '../../../../new_views/common_widgets/default_app_bar.dart'; + +class TechnicalInspectionParentAssetListView extends StatefulWidget { + final int? costCenterId; + final int? tableItemId; + final int? lineId; + + TechnicalInspectionParentAssetListView({ + Key? key, + this.costCenterId, + this.tableItemId, + this.lineId, + }) : super(key: key); + + @override + State createState() => _TechnicalInspectionParentAssetListViewState(); +} + +class _TechnicalInspectionParentAssetListViewState extends State { + final GlobalKey _formKey = GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); + bool viewOnly = false; + AssetDeliveryProvider? assetDeliveryProvider; + List assetList = []; + bool isLoading = false; + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + getInitialData(); + }); + super.initState(); + } + + void getInitialData() async { + setState(() { + isLoading = true; + }); + assetDeliveryProvider = Provider.of(context, listen: false); + //TODO replace static id's after testing... + assetList = await assetDeliveryProvider!.getParentAssetList( + costCenterId: widget.costCenterId, + tableItemId: widget.tableItemId, + lineId: widget.lineId, + ); + + setState(() { + isLoading = false; + }); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + appBar: DefaultAppBar( + title: 'Technical Inspection'.addTranslation, + titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + body:Form( + key: _formKey, + child: parentAssetList(context), // parentAssetList already returns a ListView + ), + ); + } + + Widget parentAssetList(BuildContext context) { + if (isLoading) { + return SizedBox.expand( + child: const CircularProgressIndicator(color: AppColor.primary10).center, + ); + } + + if (assetList.isEmpty) { + return const NoDataFound().center; + } + + return ListView.separated( + padding: const EdgeInsets.all(16), + itemCount: assetList.length + 1, // +1 for the "Parents" header + separatorBuilder: (_, __) => 8.height, + itemBuilder: (context, index) { + if (index == 0) { + return Text( + "Parents", + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ); + } + + final asset = assetList[index - 1]; + return TechnicalInspectionAssetCard( + assetModel: asset, + isViewOnly: viewOnly, + editPress: () async { + bool isRefreshed = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UpdateParentAssetView( + assetModel: asset, + lineId: widget.lineId, + tableItemId: widget.tableItemId, + costCenterItemId: widget.costCenterId, + ), + ), + ); + + if (isRefreshed) { + getInitialData(); + } + }, + ); + }, + ); + } +} 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 new file mode 100644 index 00000000..bdbec424 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart @@ -0,0 +1,472 @@ +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/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'; +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'; + +class UpdateChildAssetView extends StatefulWidget { + final TechnicalInspectionAssetModel assetModel; + final int? lineId; + final int index; + + UpdateChildAssetView({ + Key? key, + required this.assetModel, + required this.index, + this.lineId, + }) : super(key: key); + + @override + State createState() => _UpdateChildAssetViewState(); +} + +class _UpdateChildAssetViewState extends State { + final GlobalKey _formKey = GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); + List checkList = []; + late SiteProvider siteProvider; + late AssetDeliveryProvider assetDeliveryProvider; + late TextEditingController assetNumberCtrl; + late TextEditingController serialNoCtrl; + late TextEditingController systemIdCtrl; + + @override + void initState() { + siteProvider = Provider.of(context, listen: false); + assetDeliveryProvider = Provider.of(context, listen: false); + assetNumberCtrl = TextEditingController(); + serialNoCtrl = TextEditingController(); + systemIdCtrl = TextEditingController(); + WidgetsBinding.instance.addPostFrameCallback((_) { + populateForm(); + }); + super.initState(); + } + + void populateForm() async { + assetDeliveryProvider.isChildAssetDataLoading = true; + assetDeliveryProvider.setChildModel(widget.assetModel); + if (widget.assetModel.site != null) { + await assetDeliveryProvider.getSiteData(siteId: widget.assetModel.site?.id, model: assetDeliveryProvider.childFormModel); + } + populateTextFields(model: assetDeliveryProvider.childFormModel); + assetDeliveryProvider.isChildAssetDataLoading = false; + } + + void populateTextFields({required TechnicalInspectionAssetModel model}) { + assetNumberCtrl.text = model.assetNumber ?? ''; + serialNoCtrl.text = model.serialNo ?? ''; + systemIdCtrl.text = model.systemId ?? ''; + setState(() {}); + } + + @override + void dispose() { + assetNumberCtrl.dispose(); + serialNoCtrl.dispose(); + systemIdCtrl.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + appBar: DefaultAppBar( + title: 'Child Details'.addTranslation, + titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + body: Form( + key: _formKey, + child: Consumer(builder: (context, provider, child) { + return provider.isChildAssetDataLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Column( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + inspectionDetailsForm( + lineId: widget.lineId, + provider: provider, + ), + 8.height, + const Divider().defaultStyle(context), + 8.height, + testLines( + context: 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 testLines({required BuildContext context}) { + return 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); + }), + ], + ); + } + + /// Need to make reuseable as used in parent and child both. + + Widget inspectionDetailsForm({ + int? lineId, + required AssetDeliveryProvider provider, + }) { + TechnicalInspectionAssetModel model = provider.childFormModel; + return Column( + children: [ + AppTextFormField( + labelText: 'Asset Number'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + controller: assetNumberCtrl, + 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) { + TechnicalInspectionAssetModel? assetModel = await provider.getAssetDetails(assetId: data?.id); + + if (assetModel != null) { + provider.setChildModel(assetModel); + if (assetModel.site != null) { + await provider.getSiteData(siteId: assetModel.site?.id, model: provider.childFormModel); + } + provider.setParentModel(assetModel); + 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, + showShadow: false, + onSaved: (value) { + model.serialNo = value; + }, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: 'System ID'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + controller: systemIdCtrl, + 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: provider.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: provider.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: provider.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: provider.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 { + 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); + } + + Future openInspectionChecklist(BuildContext context) async { + applyInspectionFlagsToChecklist(model: assetDeliveryProvider.childFormModel); + + final result = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => InspectionCheckListView( + initialValues: checkList, + ))); + if (result != null) { + checkList = result; + setState(() {}); + } + buildInspectionPayload(checkList); + } + + bool _isPass(List list, String title) { + return list + .firstWhere( + (e) => e.title == title, + orElse: () => InspectionChecklistItem(status: 'FAIL'), + ) + .status == + 'PASS'; + } + + buildInspectionPayload(List checklist) { + assetDeliveryProvider.childFormModel.flagphysicalInspection = _isPass(checklist, 'Physical Inspection As Per Manufacturer Instructions'); + assetDeliveryProvider.childFormModel.flagFunctionPerformance = _isPass(checklist, 'Functional Performance As Per Results Of Manufacturer Recommended Check List'); + assetDeliveryProvider.childFormModel.flagGroundingResistance = _isPass(checklist, 'Grounding Resistance: Ω'); + assetDeliveryProvider.childFormModel.flagChassiss = _isPass(checklist, 'Chassis Leakage Current: µA'); + assetDeliveryProvider.childFormModel.flagLeadsLeakage = _isPass(checklist, 'Leads Leakage Current: µA'); + assetDeliveryProvider.childFormModel.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 TechnicalInspectionAssetModel model, + }) { + if (checkList.isEmpty) { + checkList = AssetDeliveryUtils.inspectionChecklist; + } + for (final item in checkList) { + switch (item.value) { + case 'flagphysicalInspection': + item.status = _statusFromBool(model.flagphysicalInspection); + break; + + case 'flagFunctionPerformance': + item.status = _statusFromBool(model.flagFunctionPerformance); + break; + + case 'flagGroundingResistance': + item.status = _statusFromBool(model.flagGroundingResistance); + break; + + case 'flagChassiss': + item.status = _statusFromBool(model.flagChassiss); + break; + + case 'flagLeadsLeakage': + item.status = _statusFromBool(model.flagLeadsLeakage); + break; + + case 'flagAlert': + item.status = _statusFromBool(model.flagAlert); + break; + } + } + } + + String _statusFromBool(bool? value) { + 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 new file mode 100644 index 00000000..ceafcfe6 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/update_parent_asset_view.dart @@ -0,0 +1,521 @@ +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/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'; +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_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 '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'; + +class UpdateParentAssetView extends StatefulWidget { + final TechnicalInspectionAssetModel assetModel; + final int? lineId; + final int? tableItemId; + final int? costCenterItemId; + + UpdateParentAssetView({ + Key? key, + required this.assetModel, + this.lineId, + this.tableItemId, + this.costCenterItemId, + }) : 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; + late TextEditingController assetNumberCtrl; + late TextEditingController serialNoCtrl; + late TextEditingController systemIdCtrl; + + @override + void initState() { + siteProvider = Provider.of(context, listen: false); + assetDeliveryProvider = Provider.of(context, listen: false); + assetNumberCtrl = TextEditingController(); + serialNoCtrl = TextEditingController(); + systemIdCtrl = TextEditingController(); + WidgetsBinding.instance.addPostFrameCallback((_) { + populateForm(); + }); + super.initState(); + } + + void populateForm() async { + assetDeliveryProvider.isLoading = true; + assetDeliveryProvider.setParentModel(widget.assetModel); + if (widget.assetModel.site != null) { + await assetDeliveryProvider.getSiteData(siteId: widget.assetModel.site?.id, model: assetDeliveryProvider.parentFormModel); + } else { + if (siteProvider.items.isEmpty) { + await siteProvider.getData(); + } + } + populateTextFields(model: assetDeliveryProvider.parentFormModel); + assetDeliveryProvider.isLoading = false; + } + + void populateTextFields({required TechnicalInspectionAssetModel model}) { + assetNumberCtrl.text = model.assetNumber ?? ''; + serialNoCtrl.text = model.serialNo ?? ''; + systemIdCtrl.text = model.systemId ?? ''; + setState(() {}); + } + + @override + void dispose() { + assetNumberCtrl.dispose(); + serialNoCtrl.dispose(); + systemIdCtrl.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: Consumer(builder: (context, provider, child) { + return provider.isLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Column( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + inspectionDetailsForm(lineId: widget.lineId, provider: provider), + 8.height, + const Divider().defaultStyle(context), + 8.height, + 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, + childList( + context: context, + provider: provider, + ) + ], + ).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({required BuildContext context, required AssetDeliveryProvider provider}) { + final List assetDeliveryTableList = provider.parentFormModel.childAssetList ?? []; + return ListView.separated( + itemCount: assetDeliveryTableList.length, + separatorBuilder: (_, __) => 8.height, + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (listContext, itemIndex) { + TechnicalInspectionAssetModel model = assetDeliveryTableList[itemIndex]; + return TechnicalInspectionAssetCard( + assetModel: model, + ischild: true, + editPress: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UpdateChildAssetView( + assetModel: model, + index: itemIndex, + ))); + }, + isViewOnly: false, + ); + }, + ); + } + + /// Need to make reuseable as used in parent and child both. + + Widget inspectionDetailsForm({ + int? lineId, + required AssetDeliveryProvider provider, + }) { + TechnicalInspectionAssetModel model = provider.parentFormModel; + return Column( + children: [ + //Replace with simple container... + AppTextFormField( + labelText: 'Asset Number'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + controller: assetNumberCtrl, + 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) { + TechnicalInspectionAssetModel? assetModel = await provider.getAssetDetails(assetId: data?.id); + + if (assetModel != null) { + provider.setParentModel(assetModel); + if (assetModel.site != null) { + await provider.getSiteData(siteId: assetModel.site?.id, model: provider.parentFormModel); + } + 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, + showShadow: false, + onSaved: (value) { + model.serialNo = value; + }, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: 'System ID'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + controller: systemIdCtrl, + 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: provider.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: provider.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: provider.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: provider.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, + //Need to check whare the picker start for now start from start of year + from: DateTime(DateTime.now().year, 1, 1), + 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 { + 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; + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await assetDeliveryProvider.saveTechnicalInspectionAssetData(model: assetDeliveryProvider.parentFormModel).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context, true); + } + }); + } + + Future openInspectionChecklist(BuildContext context) async { + applyInspectionFlagsToChecklist(model: assetDeliveryProvider.parentFormModel); + final result = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => InspectionCheckListView( + initialValues: checkList, + ))); + + if (result != null) { + checkList = result; + setState(() {}); + } + buildInspectionPayload(checkList); + } + + bool _isPass(List list, String title) { + return list + .firstWhere( + (e) => e.title == title, + orElse: () => InspectionChecklistItem(status: 'FAIL'), + ) + .status == + 'PASS'; + } + + buildInspectionPayload(List checklist) { + assetDeliveryProvider.parentFormModel.flagphysicalInspection = _isPass(checklist, 'Physical Inspection As Per Manufacturer Instructions'); + assetDeliveryProvider.parentFormModel.flagFunctionPerformance = _isPass(checklist, 'Functional Performance As Per Results Of Manufacturer Recommended Check List'); + assetDeliveryProvider.parentFormModel.flagGroundingResistance = _isPass(checklist, 'Grounding Resistance: Ω'); + assetDeliveryProvider.parentFormModel.flagChassiss = _isPass(checklist, 'Chassis Leakage Current: µA'); + assetDeliveryProvider.parentFormModel.flagLeadsLeakage = _isPass(checklist, 'Leads Leakage Current: µA'); + assetDeliveryProvider.parentFormModel.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 TechnicalInspectionAssetModel model, + }) { + if (checkList.isEmpty) { + checkList = AssetDeliveryUtils.inspectionChecklist; + } + for (final item in checkList) { + switch (item.value) { + case 'flagphysicalInspection': + item.status = _statusFromBool(model.flagphysicalInspection); + break; + + case 'flagFunctionPerformance': + item.status = _statusFromBool(model.flagFunctionPerformance); + break; + + case 'flagGroundingResistance': + item.status = _statusFromBool(model.flagGroundingResistance); + break; + + case 'flagChassiss': + item.status = _statusFromBool(model.flagChassiss); + break; + + case 'flagLeadsLeakage': + item.status = _statusFromBool(model.flagLeadsLeakage); + break; + + case 'flagAlert': + 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 new file mode 100644 index 00000000..035fffe3 --- /dev/null +++ b/lib/modules/asset_delivery_module/pages/technical_inpection/update_technical_inspection_lines_view.dart @@ -0,0 +1,285 @@ +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/lookup.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/delivery_line_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_form_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_cost_center_card_view.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_parent_asset_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/power_supply_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/single_item_drop_down_menu.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; +import '../../../../new_views/common_widgets/app_filled_button.dart'; +import '../../../../new_views/common_widgets/default_app_bar.dart'; + +class UpdateTechnicalInspectionLinesView extends StatefulWidget { + static const String id = "/technical-inspection-form"; + final AssetDeliveryTableModel deliveryTableModel; + final AssetDeliveryDataModel? requestModel; + final DeliveryLineModel? deliveryLineModel; + + UpdateTechnicalInspectionLinesView({ + Key? key, + required this.deliveryTableModel, + this.requestModel, + this.deliveryLineModel, + }) : super(key: key); + + @override + State createState() => _UpdateTechnicalInspectionLinesViewState(); +} + +class _UpdateTechnicalInspectionLinesViewState extends State { + final GlobalKey _formKey = GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); + TechnicalInspectionFormModel model = TechnicalInspectionFormModel(); + AssetDeliveryProvider? assetDeliveryProvider; + bool isLoading = false; + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + getInitialData(); + }); + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + void getInitialData() async { + assetDeliveryProvider = Provider.of(context, listen: false); + isLoading = true; + setState(() {}); + model = await assetDeliveryProvider!.getTechnicalInspectionData( + lineId: widget.deliveryLineModel?.id, + tableId: widget.deliveryTableModel.id, + ); + isLoading = false; + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + appBar: DefaultAppBar( + title: 'Technical Inspection'.addTranslation, + titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + body: Form( + key: _formKey, + child: isLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Column( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + itemDetails(context: context, itemNo: widget.deliveryLineModel?.itemNumber, itemDescription: widget.deliveryLineModel?.itemDescription), + 12.height, + inspectionDetailsForm(), + 8.height, + const Divider().defaultStyle(context), + Text( + "Cost Centers", + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.height, + costCenterList(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 costCenterList(BuildContext context) { + List costCenterList = model.costCenters ?? []; + return costCenterList.isEmpty + ? const NoDataFound().center + : ListView.separated( + itemCount: costCenterList.length, + separatorBuilder: (_, __) => 8.height, + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (listContext, itemIndex) { + return TechnicalInspectionCostCard( + costModel: costCenterList[itemIndex], + editPress: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => TechnicalInspectionParentAssetListView( + costCenterId: costCenterList[itemIndex].id, + tableItemId: widget.deliveryTableModel.id, + lineId: widget.deliveryLineModel?.id, + ))); + }, + isViewOnly: false, + ); + }, + ); + } + + 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), + ), + 8.height, + Text( + itemDescription ?? '-', + style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120), + ), + ], + ); + } + + Widget inspectionDetailsForm() { + return Column( + children: [ + SingleItemDropDownMenu( + context: context, + height: 56.toScreenHeight, + title: 'Power Supply Type'.addTranslation, + titleTextColor: AppColor.neutral120, + showShadow: false, + validator: (value) { + if (value == null) return 'Please select power supply type'; + return null; + }, + backgroundColor: AppColor.fieldBgColor(context), + showAsBottomSheet: true, + initialValue: model.powerSupplyType, + onSelect: (value) { + if (value != null) { + model.powerSupplyType = value; + setState(() {}); + } + }, + ), + 8.height, + AppTextFormField( + labelText: 'Power Reading'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.powerReading != null ? model.powerReading.toString() : '', + textAlign: TextAlign.center, + labelStyle: AppTextStyles.textFieldLabelStyle, + textInputType: TextInputType.number, + showShadow: false, + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + onSaved: (value) { + model.powerReading = int.tryParse(value); + }, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: 'Max Current'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.maxCurrent != null ? model.maxCurrent.toString() : '', + textAlign: TextAlign.center, + labelStyle: AppTextStyles.textFieldLabelStyle, + textInputType: TextInputType.number, + showShadow: false, + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + onSaved: (value) { + model.maxCurrent = int.tryParse(value); + }, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: 'Voltage'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + textInputType: TextInputType.number, + initialValue: model.voltage != null ? model.voltage.toString() : '', + textAlign: TextAlign.center, + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + onSaved: (value) { + model.voltage = int.tryParse(value); + }, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: 'Frequency'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.frequency != null ? model.frequency.toString() : '', + textInputType: TextInputType.number, + textAlign: TextAlign.center, + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + onSaved: (value) { + model.frequency = int.tryParse(value); + }, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ); + } + + void _saveTap() async { + if (!_formKey.currentState!.validate()) return; + _formKey.currentState!.save(); + model.tableItemId = widget.deliveryTableModel.id; + model.lineId = widget.deliveryLineModel?.id; + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await assetDeliveryProvider?.saveTechnicalInspection(model: model).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context); + } + }); + } +} diff --git a/lib/modules/asset_delivery_module/provider/asset_delivery_provider.dart b/lib/modules/asset_delivery_module/provider/asset_delivery_provider.dart new file mode 100644 index 00000000..6516b500 --- /dev/null +++ b/lib/modules/asset_delivery_module/provider/asset_delivery_provider.dart @@ -0,0 +1,736 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'package:flutter/material.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'; +import 'package:test_sa/models/generic_attachment_model.dart'; +import 'package:test_sa/models/module_permissions_model.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/room_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/cost_center_serial_no_form_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/delivery_inspection_form_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/delivery_line_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/end_user_asset_details_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/history_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_asset_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_form_model.dart'; +import '../../../models/new_models/department.dart'; +import '../../../models/new_models/site.dart'; +import '../models/oracle_user_model.dart'; + +class AssetDeliveryProvider extends ChangeNotifier { + final pageItemNumber = 10; + final searchPageItemNumber = 10; + int pageNo = 1; + AssetDeliveryDataModel? assetDeliveryDataModel; + List assetDeliveryTableList = []; + List deliveryLinesList = []; + ModulesPermissionsModel? modelPermission; + TechnicalInspectionAssetModel parentFormModel = TechnicalInspectionAssetModel(); + TechnicalInspectionAssetModel childFormModel = TechnicalInspectionAssetModel(); + + void reset() { + pageNo = 1; + stateCode = null; + } + + int? stateCode; + bool isDetailLoading = false; + bool nextPage = false; + bool isLoading = false; + bool isSiteLoading = false; + bool isDeliveryTableListLoading = false; + bool isDeliveryLineListLoading = false; + bool isChildAssetDataLoading = false; + + void setParentModel(TechnicalInspectionAssetModel m) { + parentFormModel = m; + notifyListeners(); + } + + void setChildModel(TechnicalInspectionAssetModel m) { + childFormModel = m; + notifyListeners(); + } + + Future getAssetDeliveryDetailById({int? requestId}) async { + try { + assetDeliveryDataModel = AssetDeliveryDataModel(); + isLoading = true; + notifyListeners(); + final response = await ApiManager.instance.get(URLs.getAssetDeliveryDetailsById + "?Id=$requestId"); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + assetDeliveryDataModel = AssetDeliveryDataModel.fromJson(json.decode(response.body)["data"]); + } else { + assetDeliveryDataModel = null; + } + isLoading = false; + notifyListeners(); + return assetDeliveryDataModel; + } catch (e) { + log("getdelivery details [error] : $e"); + isLoading = false; + assetDeliveryDataModel = null; + notifyListeners(); + return assetDeliveryDataModel; + } + } + + Future getAssetDeliveryTableListById({int? requestId}) async { + try { + assetDeliveryTableList.clear(); + isDeliveryTableListLoading = true; + notifyListeners(); + + final response = await ApiManager.instance.get( + "${URLs.getAssetDeliveryTableListById}?assetDeliveryExternalMasterId=$requestId", + ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + final jsonData = json.decode(response.body)["data"]; + if (jsonData is List) { + assetDeliveryTableList = jsonData.map((e) => AssetDeliveryTableModel.fromJson(e)).toList(); + } else { + assetDeliveryTableList = []; + } + } else { + assetDeliveryTableList = []; + } + + isDeliveryTableListLoading = false; + notifyListeners(); + } catch (e) { + log("get Delivery Table details [error] : $e"); + isDeliveryTableListLoading = false; + assetDeliveryTableList = []; + notifyListeners(); + } + } + + Future getDeliveryLinesListById({int? itemId}) async { + try { + deliveryLinesList.clear(); + isDeliveryLineListLoading = true; + notifyListeners(); + final response = await ApiManager.instance.get( + "${URLs.getLineFromExternalPo}?assetDeliveryExternalDeliveryId=$itemId", + ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + final jsonData = json.decode(response.body)["data"]; + if (jsonData is List) { + deliveryLinesList = jsonData.map((e) => DeliveryLineModel.fromJson(e)).toList(); + } else { + deliveryLinesList = []; + } + } else { + deliveryLinesList = []; + } + isDeliveryLineListLoading = false; + notifyListeners(); + } catch (e) { + log("get Delivery Lines List [error] : $e"); + isDeliveryLineListLoading = false; + deliveryLinesList = []; + notifyListeners(); + } + } + + Future> getTechnicalInspectionLinesListById({int? itemId}) async { + List linesList = []; + try { + isLoading = true; + notifyListeners(); + final response = await ApiManager.instance.get( + "${URLs.getLineFromTechnicalInspection}?assetDeliveryExternalDeliveryId=$itemId", + ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + final jsonData = json.decode(response.body)["data"]; + if (jsonData is List) { + linesList = jsonData.map((e) => DeliveryLineModel.fromJson(e)).toList(); + } else { + assetDeliveryTableList = []; + } + } else { + linesList = []; + } + isLoading = false; + notifyListeners(); + return linesList; + } catch (e) { + log("get Delivery Lines List [error] : $e"); + isLoading = false; + linesList = []; + notifyListeners(); + return linesList; + } + } + + Future getAssetDeliveryCostCenterListForLines({ + int? lineId, + int? tableItemId, + }) async { + CostCenterSerialNoFormModel costCenterSerialNoFormModel = CostCenterSerialNoFormModel(); + try { + isLoading = true; + notifyListeners(); + + final response = await ApiManager.instance.get( + "${URLs.getCostCenterAndSerialNoToLineDeliveryInspection}?AssetDeliveryExternalDetailId=$lineId&AssetDeliveryExternalDeliveryId=$tableItemId", + ); + + stateCode = response.statusCode; + + if (response.statusCode >= 200 && response.statusCode < 300) { + final jsonData = json.decode(response.body)["data"]; + costCenterSerialNoFormModel = CostCenterSerialNoFormModel.fromJson(jsonData); + } else { + costCenterSerialNoFormModel.costCenters = []; + costCenterSerialNoFormModel.serialNos = []; + } + } catch (e) { + log("get Delivery Lines List [error] : $e"); + costCenterSerialNoFormModel.costCenters = []; + costCenterSerialNoFormModel.serialNos = []; + } finally { + isLoading = false; + notifyListeners(); + } + + return costCenterSerialNoFormModel; + } + + Future addNewDelivery({ + int? requestId, + }) async { + isDeliveryTableListLoading = true; + Map payload = { + 'assetDeliveryExternalMasterId': requestId, + }; + try { + final response = await ApiManager.instance.post(URLs.addNewDelivery, body: payload); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isDeliveryTableListLoading = false; + notifyListeners(); + return true; + } else { + isDeliveryTableListLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isDeliveryTableListLoading = false; + notifyListeners(); + return false; + } + } + + Future saveTechnicalInspection({ + required TechnicalInspectionFormModel model, + }) async { + isLoading = true; + try { + final response = await ApiManager.instance.post(URLs.saveTechnicalInspection, body: model.toJson()); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return false; + } + } + + Future saveCostCenterToLineDeliveryInspection({ + 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.saveCostCenterAndSerialNoToLineDeliveryInspection, body: payload); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return false; + } + } + + Future changeDeliveryStatusToCancel({int? itemId}) async { + isLoading = true; + try { + final response = await ApiManager.instance.get( + "${URLs.changeDeliveryStatusToCancel}?assetDeliveryExternalDeliveryId=$itemId", + ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return false; + } + } + + Future changeDeliveryStatus({ + int? itemId, + Map? requestPayload, + }) async { + isLoading = true; + Map payload = { + 'assetDeliveryExternalDeliveryId': itemId, + }; + try { + final response = await ApiManager.instance.post(URLs.changeDeliveryStatus, body: requestPayload ?? payload); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return false; + } + } + + Future getAssetDeliveryInspectionData({int? itemId}) async { + isLoading = true; + DeliveryInspectionFormModel deliveryInspectionFormModel = DeliveryInspectionFormModel(); + try { + final response = await ApiManager.instance.get( + "${URLs.getAssetDeliveryInspectionData}?assetDeliveryExternalDeliveryId=$itemId", + ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + deliveryInspectionFormModel = DeliveryInspectionFormModel.fromJson(json.decode(response.body)["data"]); + } + isLoading = false; + notifyListeners(); + return deliveryInspectionFormModel; + } catch (e) { + log("getAssetDeliveryInspectionData details [error] : $e"); + isLoading = false; + notifyListeners(); + return deliveryInspectionFormModel; + } + } + + Future getTechnicalInspectionData({int? lineId, int? tableId}) async { + isLoading = true; + TechnicalInspectionFormModel technicalInspectionFormModel = TechnicalInspectionFormModel(); + try { + final response = await ApiManager.instance.get( + "${URLs.getTechnicalInspectionByDetailId}?assetDeliveryExternalDetailId=$lineId&assetDeliveryExternalDeliveryId=$tableId", + ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + technicalInspectionFormModel = TechnicalInspectionFormModel.fromJson(json.decode(response.body)["data"]); + } + isLoading = false; + notifyListeners(); + return technicalInspectionFormModel; + } catch (e) { + log("getTechnicalInspectionByDetailId details [error] : $e"); + isLoading = false; + notifyListeners(); + return technicalInspectionFormModel; + } + } + + Future getDeliveryHistories({int? itemId}) async { + isLoading = true; + DeliveryInspectionFormModel deliveryInspectionFormModel = DeliveryInspectionFormModel(); + try { + final response = await ApiManager.instance.get( + "${URLs.getAssetDeliveryInspectionData}?assetDeliveryExternalDeliveryId=$itemId", + ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + deliveryInspectionFormModel = DeliveryInspectionFormModel.fromJson(json.decode(response.body)["data"]); + } + isLoading = false; + notifyListeners(); + return deliveryInspectionFormModel; + } catch (e) { + log("getAssetDeliveryInspectionData details [error] : $e"); + isLoading = false; + notifyListeners(); + return deliveryInspectionFormModel; + } + } + + Future> getOracleUserAutoCompleteDetails({ + String? query, + }) async { + try { + isLoading = true; + final response = await ApiManager.instance.post(URLs.getOracleUserList, body: {'employeeName': query}); + List list = []; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + final data = json.decode(response.body)["data"]; + list = (data as List).map((e) => OracleUserModel.fromJson(e)).toList(); + } + isLoading = false; + notifyListeners(); + return list; + } catch (error) { + log('error $error'); + isLoading = false; + notifyListeners(); + return []; + } + } + + Future> getAttachments({ + num? tableItemId, + num? attachmentType, + }) async { + try { + isLoading = true; + final response = await ApiManager.instance.get("${URLs.getAttachments}?attachmentDocTypeValue=$attachmentType&assetDeliveryExternalDeliveryId=$tableItemId"); + List list = []; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + final data = json.decode(response.body)["data"]; + list = (data as List).map((e) => GenericAttachmentModel.fromJson(e)).toList(); + } + isLoading = false; + notifyListeners(); + return list; + } catch (error) { + log('error $error'); + isLoading = false; + notifyListeners(); + return []; + } + } + + Future> getParentAssetList({int? costCenterId, int? tableItemId, int? lineId}) async { + List parentAssetList = []; + try { + isLoading = true; + notifyListeners(); + final response = await ApiManager.instance.get( + "${URLs.getTechnicalInspectionParentData}?assetDeliveryExternalDeliveryId=$tableItemId&assetDeliveryExternalDetailId=$lineId&costCenterId=$costCenterId", + ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + final jsonData = json.decode(response.body)["data"]; + if (jsonData is List) { + parentAssetList = jsonData.map((e) => TechnicalInspectionAssetModel.fromJson(e)).toList(); + } + } else { + parentAssetList = []; + } + isLoading = false; + notifyListeners(); + return parentAssetList; + } catch (e) { + log("get parent List [error] : $e"); + isLoading = false; + parentAssetList = []; + notifyListeners(); + return parentAssetList; + } + } + + Future getSiteData({ + required num? siteId, + required TechnicalInspectionAssetModel model, + }) async { + isSiteLoading = true; + notifyListeners(); + + try { + final response = await ApiManager.instance.get( + "${URLs.getSiteAutoCompleteWithoutConditionSites}?id=$siteId", + ); + + stateCode = response.statusCode; + + if (response.statusCode >= 200 && response.statusCode < 300) { + final List? siteData = json.decode(response.body)["data"]; + + if (siteData != null && siteData.isNotEmpty) { + model.site = Site.fromJson(siteData.first); + } + + model.building = model.site?.buildings?.firstWhere( + (e) => e.value == model.building?.value, + orElse: () => Building(), + ); + + model.floor = model.building?.floors?.firstWhere( + (e) => e.value == model.floor?.value, + orElse: () => Floor(), + ); + + model.department = model.floor?.departments?.firstWhere( + (e) => e.departmentName == model.department?.departmentName, + orElse: () => Department(), + ); + + model.room = model.department?.rooms?.firstWhere( + (e) => e.value == model.room?.value, + orElse: () => Rooms(), + ); + } + + isSiteLoading = false; + notifyListeners(); + return model; + } catch (e) { + isSiteLoading = false; + stateCode = -1; + notifyListeners(); + return model; + } + } + + Future getAssetDetails({required num? assetId}) async { + isLoading = true; + notifyListeners(); + try { + Response response = await ApiManager.instance.get(URLs.getAssetDetailsByAssetId + "?AssetDeliveryInternalDetailAssetId=$assetId"); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + final data = json.decode(response.body)["data"]; + TechnicalInspectionAssetModel assetModel = TechnicalInspectionAssetModel.fromJson(data); + isLoading = false; + notifyListeners(); + return assetModel; + } else { + return null; + } + } catch (error) { + isLoading = false; + stateCode = -1; + notifyListeners(); + return null; + } + } + + Future saveTechnicalInspectionAssetData({ + required TechnicalInspectionAssetModel model, + }) async { + isLoading = true; + try { + final response = await ApiManager.instance.post(URLs.saveTechnicalInspectionAsset, body: model.toJson()); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return false; + } + } + + Future saveAttachment({ + required Map payload, + }) async { + isLoading = true; + try { + final response = await ApiManager.instance.post(URLs.saveAttachments, body: payload); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return false; + } + } + + Future> getHistory({ + num? tableItemId, + }) async { + try { + isLoading = true; + notifyListeners(); + final response = await ApiManager.instance.get("${URLs.getDeliveryHistories}?assetDeliveryExternalDeliveryId=$tableItemId"); + List list = []; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + final data = json.decode(response.body)["data"]; + list = (data as List).map((e) => AssetDeliveryHistoryModel.fromJson(e)).toList(); + } + isLoading = false; + notifyListeners(); + return list; + } catch (error) { + log('error $error'); + isLoading = false; + notifyListeners(); + return []; + } + } + + //End user + + Future> getEndUserCostCenterList({int? tableItemId}) async { + List costCenterList = []; + try { + isLoading = true; + notifyListeners(); + final response = await ApiManager.instance.get( + "${URLs.getEndUserAcceptanceCostCenter}?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(); + } + } else { + costCenterList = []; + } + isLoading = false; + notifyListeners(); + return costCenterList; + } catch (e) { + log("get costCenter for End user [error] : $e"); + isLoading = false; + costCenterList = []; + notifyListeners(); + return costCenterList; + } + } + + Future> getEndUserAssetList({int? tableItemId, String? costCenterName}) async { + List assetList = []; + try { + isLoading = true; + notifyListeners(); + final response = await ApiManager.instance.get( + "${URLs.getCostCenterAssetList}?assetDeliveryExternalDeliveryId=$tableItemId&costCenterName=$costCenterName", + ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + final jsonData = json.decode(response.body)["data"]; + if (jsonData is List) { + assetList = jsonData.map((e) => EndUserAssetDetailsModel.fromJson(e)).toList(); + } + } else { + assetList = []; + } + isLoading = false; + notifyListeners(); + return assetList; + } catch (e) { + log("get End User AssetList [error] : $e"); + isLoading = false; + assetList = []; + notifyListeners(); + return assetList; + } + } + + Future saveEndUserFormData({ + required CostCenterModel model, + }) async { + isLoading = true; + try { + final response = await ApiManager.instance.post(URLs.saveEndUserFormData, body: model.toEndUserAcceptanceFormJson()); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return false; + } + } + + Future saveEndUserAssetDataData({ + required EndUserAssetDetailsModel model, + }) async { + isLoading = true; + try { + final response = await ApiManager.instance.post(URLs.saveEndUserAssetData, body: model.toJson()); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return 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/asset_delivery_module/provider/cost_center_provider.dart b/lib/modules/asset_delivery_module/provider/cost_center_provider.dart new file mode 100644 index 00000000..80cac313 --- /dev/null +++ b/lib/modules/asset_delivery_module/provider/cost_center_provider.dart @@ -0,0 +1,45 @@ +import 'dart:convert'; +import 'dart:developer'; + +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/modules/loan_module/models/medical_department_model.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; + +class CostCenterProvider extends LoadingListNotifier { + @override + Future getData({int? id}) async { + if (loading) return; + loading = true; + notifyListeners(); + try { + final response = await ApiManager.instance.get( + URLs.getCostCenterToPo + "?assetDeliveryExternalMasterId=$id", + ); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + final List list = json.decode(response.body)['data']; + final Set seenNames = {}; + int index = 0; + items = + list.where((e) => e is Map && e['name'] != null && e['name'].toString().trim().isNotEmpty).map((e) => e['name'].toString().trim()).where((name) => seenNames.add(name)).map((name) { + final lookup = Lookup( + id: index, + value: index + 1, + name: name, + ); + index++; + return lookup; + }).toList(); + } + } catch (e, s) { + log('getData error', error: e, stackTrace: s); + stateCode = -1; + } finally { + loading = false; + notifyListeners(); + } + } +} diff --git a/lib/modules/asset_delivery_module/provider/end_user_rejection_reason_lookup_provider.dart b/lib/modules/asset_delivery_module/provider/end_user_rejection_reason_lookup_provider.dart new file mode 100644 index 00000000..f560e57e --- /dev/null +++ b/lib/modules/asset_delivery_module/provider/end_user_rejection_reason_lookup_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 EndUserRejectionReasonLookupProvider 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.endUserRejectionReasonLookup); + 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/asset_delivery_module/provider/end_user_status_lookup_provider.dart b/lib/modules/asset_delivery_module/provider/end_user_status_lookup_provider.dart new file mode 100644 index 00000000..866cc600 --- /dev/null +++ b/lib/modules/asset_delivery_module/provider/end_user_status_lookup_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 EndUserStatusLookupProvider 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.endUserStatusLookup); + 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/asset_delivery_module/provider/power_supply_type_provider.dart b/lib/modules/asset_delivery_module/provider/power_supply_type_provider.dart new file mode 100644 index 00000000..353b3ae2 --- /dev/null +++ b/lib/modules/asset_delivery_module/provider/power_supply_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 PowerSupplyTypeProvider 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.powerSupplyTypeLookUp); + 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 d396e598..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,85 +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(() { - CMDetailProvider provider = Provider.of(context, listen: false); - Navigator.push(context, MaterialPageRoute(builder: (context) => CreateLoanRequestPage(workOrderId: widget.requestId, workOrder: provider.currentWorkOrder!.data))); - }); - } - 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/bottom_sheets/service_request_bottomsheet.dart b/lib/modules/cm_module/views/components/bottom_sheets/service_request_bottomsheet.dart index 95e1d3ce..f149de8a 100644 --- a/lib/modules/cm_module/views/components/bottom_sheets/service_request_bottomsheet.dart +++ b/lib/modules/cm_module/views/components/bottom_sheets/service_request_bottomsheet.dart @@ -385,7 +385,7 @@ class ServiceRequestBottomSheet { ], ); }, - ).toShimmer(isShow: snapshot.loading,context: context); + ).toShimmer(isShow: snapshot.loading, context: context); }); } @@ -645,7 +645,7 @@ class ServiceRequestBottomSheet { borderRadius: BorderRadius.circular(14), // Circular border radius ), // color: AppColor.neutral80, - color: AppColor.background(context), + color: AppColor.background(context), child: Row( crossAxisAlignment: CrossAxisAlignment.start, // Align items at the top children: [ @@ -1191,30 +1191,26 @@ class ServiceRequestBottomSheet { 10.height, StatefulBuilder( builder: (context, setState) { - return Column( - children: [ - ESignature( - title: '', - oldSignature: '', - newSignature: newSignature, - backgroundColor: AppColor.neutral100, - showShadow: false, - onChange: (signature) { - if (signature.isEmpty) return; - setState(() { - newSignature = signature; - nurseSignature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}"; - }); - }, - onSaved: (signature) { - if (signature == null || signature.isEmpty) return; - setState(() { - newSignature = signature; - nurseSignature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}"; - }); - }, - ), - ], + return ESignature( + title: '', + oldSignature: '', + newSignature: newSignature, + backgroundColor: AppColor.neutral100, + showShadow: false, + onChange: (signature) { + if (signature.isEmpty) return; + setState(() { + newSignature = signature; + nurseSignature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}"; + }); + }, + onSaved: (signature) { + if (signature == null || signature.isEmpty) return; + setState(() { + newSignature = signature; + nurseSignature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}"; + }); + }, ); }, ), 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 4d28bd1b..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() { @@ -74,24 +79,31 @@ class _ChatWidgetState extends State { @override Widget build(BuildContext context) { return widget.isShow - ? Consumer(builder: (pContext, requestProvider, _) { - return IconButton( - icon: const Icon(Icons.chat_bubble), - onPressed: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => ChatPage( - moduleId: widget.moduleId, - requestId: widget.requestId, - title: widget.title, - readOnly: widget.isReadOnly, - assigneeEmployeeNumber: widget.assigneeEmployeeNumber, - contactEmployeeINumber: widget.contactEmployeeINumber, - myLoginUserID: widget.myLoginUserID, - ))); - }, - ).toShimmer(context: context, isShow: requestProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30); + ? Consumer(builder: (pContext, chatProvider, _) { + final int unreadCount = (!chatProvider.chatLoginTokenLoading && (chatProvider.chatParticipantModel?.unreadCount ?? 0) > 0) ? chatProvider.chatParticipantModel!.unreadCount! : 0; + return CustomBadge2( + value: unreadCount, + top: 2, + right: 2, + minSize: 18, + child: IconButton( + icon: const Icon(Icons.chat_bubble), + onPressed: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ChatPage( + moduleId: widget.moduleId, + requestId: widget.requestId, + title: widget.title, + readOnly: widget.isReadOnly, + assigneeEmployeeNumber: widget.assigneeEmployeeNumber, + contactEmployeeINumber: widget.contactEmployeeINumber, + myLoginUserID: widget.myLoginUserID, + ))); + }, + ).toShimmer(context: context, isShow: chatProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30), + ); }) : const SizedBox(); } diff --git a/lib/modules/cx_module/chat/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 04fba4a1..6d9c99d9 100644 --- a/lib/modules/cx_module/chat/helper/chat_file_viewer.dart +++ b/lib/modules/cx_module/chat/helper/chat_file_viewer.dart @@ -24,6 +24,7 @@ class ChatFileViewer extends StatelessWidget { @override Widget build(BuildContext context) { + return FutureBuilder( future: checkFileInLocalStorage(), builder: (BuildContext context, AsyncSnapshot snapshot) { 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/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/pages/create_loan_request_page.dart b/lib/modules/loan_module/pages/create_loan_request_page.dart index fe76e39b..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,39 +1,33 @@ import 'dart:convert'; import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:test_sa/controllers/providers/api/device_transfer_provider.dart'; import 'package:test_sa/controllers/validator/validator.dart'; -import 'package:test_sa/dashboard_latest/dashboard_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/helper/utils.dart'; -import 'package:test_sa/models/device/asset.dart'; -import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/generic_attachment_model.dart'; import 'package:test_sa/models/lookup.dart'; -import 'package:test_sa/models/new_models/site.dart'; +import 'package:test_sa/models/new_models/mapped_sites.dart'; import 'package:test_sa/models/new_models/task_request/task_type_model.dart'; +import 'package:test_sa/models/new_models/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/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'; @@ -275,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"; @@ -287,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, @@ -301,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(() {}); }, ), diff --git a/lib/modules/medical_gas_inspection/create_medical_gas_request_page.dart b/lib/modules/medical_gas_inspection/create_medical_gas_request_page.dart index 40f47f5e..0bbd9e66 100644 --- a/lib/modules/medical_gas_inspection/create_medical_gas_request_page.dart +++ b/lib/modules/medical_gas_inspection/create_medical_gas_request_page.dart @@ -1,49 +1,37 @@ -import 'dart:convert'; -import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:provider/provider.dart'; -import 'package:test_sa/controllers/providers/api/device_transfer_provider.dart'; -import 'package:test_sa/controllers/validator/validator.dart'; -import 'package:test_sa/dashboard_latest/dashboard_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; -import 'package:test_sa/helper/utils.dart'; -import 'package:test_sa/models/device/asset.dart'; -import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/generic_attachment_model.dart'; import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/new_models/site.dart'; import 'package:test_sa/models/new_models/task_request/task_type_model.dart'; -import 'package:test_sa/models/new_models/work_order_detail_model.dart'; -import 'package:test_sa/models/service_request/supplier_details.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/modules/medical_gas_inspection/models/medical_gas_data_model.dart'; +import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_form_model.dart'; +import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart'; +import 'package:test_sa/modules/medical_gas_inspection/providers/item_type_provider.dart'; +import 'package:test_sa/modules/medical_gas_inspection/providers/medical_gas_inspection_provider.dart'; +import 'package:test_sa/modules/medical_gas_inspection/providers/medical_gas_supplier_provider.dart'; +import 'package:test_sa/modules/medical_gas_inspection/providers/order_type_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_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 '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/work_order/vendor_provider.dart'; -import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; import '../../../../../../new_views/common_widgets/default_app_bar.dart'; -import 'medical_gas_inspection_detail_page.dart'; class CreateMedicalGasRequestPage extends StatefulWidget { static const String id = "/create-medical-gas"; + final MedicalGasDataModel? dataModel; - CreateMedicalGasRequestPage({Key? key}) : super(key: key); + CreateMedicalGasRequestPage({Key? key, this.dataModel}) : super(key: key); @override _CreateMedicalGasRequestPageState createState() => _CreateMedicalGasRequestPageState(); @@ -54,20 +42,40 @@ class _CreateMedicalGasRequestPageState extends State _formKey = GlobalKey(); final GlobalKey _scaffoldKey = GlobalKey(); TaskType? selectedType; - final LoanFormModel _loanFormModel = LoanFormModel(); - - TextEditingController commentController = TextEditingController(); - - SupplierDetails? supplier; - - // List loanTypes = []; + MedicalGasFormModel _medicalGasFormModel = MedicalGasFormModel(); + MedicalGasSupplierModel? medicalGasInfo; + late MedicalGasInspectionProvider medicalGasInspectionProvider; + bool isGasInfoLoading = false; @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)]; + // Provider.of(context, listen: false).reset(); + medicalGasInspectionProvider = Provider.of(context, listen: false); + _populateFormData(); + } + + _populateFormData() { + if (widget.dataModel != null) { + MedicalGasDataModel dataModel = widget.dataModel!; + _medicalGasFormModel = MedicalGasFormModel( + id: dataModel.id, + orderType: Lookup(id: dataModel.requestOrderTypeId, name: dataModel.requestOrderType), + itemType: Lookup(id: dataModel.itemTypeId, name: dataModel.itemType), + site: Site(id: dataModel.siteId, custName: dataModel.site), + supplier: MedicalGasSupplierModel(id: dataModel.supplierId, supplier: dataModel.supplier), + ); + List attributeInfo = []; + dataModel.requestDetailDtos.forEach((v) { + attributeInfo.add(AttributeInfo.fromJson(v.toJson())); + }); + medicalGasInfo = MedicalGasSupplierModel( + supplierContact: dataModel.contact, + supplierEmail: dataModel.email, + attributeInfo: attributeInfo, + ); + setState(() {}); + } } @override @@ -85,42 +93,44 @@ class _CreateMedicalGasRequestPageState extends State( + SingleItemDropDownMenu( context: context, title: "Order Type", validator: (value) { if (value == null) return "Mandatory"; return null; }, - enabled: false, - staticData: [], - initialValue: null, + initialValue: _medicalGasFormModel.orderType, showShadow: false, backgroundColor: AppColor.fieldBgColor(context), showAsBottomSheet: true, - onSelect: (value) {}, + onSelect: (value) { + _medicalGasFormModel.orderType = value; + }, ), 8.height, - SingleItemDropDownMenu( + SingleItemDropDownMenu( context: context, title: "Item Type", validator: (value) { if (value == null) return "Mandatory"; return null; }, - enabled: false, - staticData: [], - initialValue: null, + initialValue: _medicalGasFormModel.itemType, showShadow: false, backgroundColor: AppColor.fieldBgColor(context), showAsBottomSheet: true, - onSelect: (value) {}, + onSelect: (value) { + _medicalGasFormModel.itemType = value; + setState(() {}); + setGasInfo(); + }, ), 8.height, SingleItemDropDownMenu( context: context, title: context.translation.site, - initialValue: _loanFormModel.site, + initialValue: _medicalGasFormModel.site, showShadow: false, validator: (value) { if (value == null) return "Please select a site"; @@ -129,71 +139,72 @@ class _CreateMedicalGasRequestPageState extends State( - context: context, - title: context.translation.department, - showShadow: false, - validator: (value) { - if (value == null) return "Please select a department"; - return null; - }, - showAsBottomSheet: true, - initialValue: _loanFormModel.department, - requestById: context.userProvider.user?.clientId, - backgroundColor: AppColor.fieldBgColor(context), - onSelect: (value) { - _loanFormModel.department = value; - setState(() {}); - }, - ), - 8.height, - SingleItemDropDownMenu( + // 8.height, + // SingleItemDropDownMenu( + // context: context, + // title: context.translation.department, + // showShadow: false, + // validator: (value) { + // if (value == null) return "Please select a department"; + // return null; + // }, + // showAsBottomSheet: true, + // initialValue: _medicalGasFormModel.department, + // requestById: context.userProvider.user?.clientId, + // backgroundColor: AppColor.fieldBgColor(context), + // onSelect: (value) { + // _medicalGasFormModel.department = value; + // setState(() {}); + // }, + // ), + // 8.height, + SingleItemDropDownMenu( context: context, title: context.translation.supplier, - backgroundColor: AppColor.fieldBgColor(context), - initialValue: supplier, + initialValue: _medicalGasFormModel.supplier, showAsBottomSheet: true, + backgroundColor: AppColor.fieldBgColor(context), showShadow: false, - showCancel: true, - onSelect: (_supplier) { - supplier = _supplier; - setState(() {}); - }, - ), - 8.height, - if (supplier != null) ...[ - 'Name: ${supplier?.name ?? "-"}'.addTranslation.bodyText(context).custom(color: AppColor.black10), - 'Email: ${supplier?.email ?? "-"}'.addTranslation.bodyText(context).custom(color: AppColor.black10), - 'Contact: ${supplier?.contact ?? "-"}'.addTranslation.bodyText(context).custom(color: AppColor.black10), - ], - 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; - setState(() {}); + onSelect: (value) { + setState(() { + _medicalGasFormModel.supplier = value; + }); + setGasInfo(); }, ), + + if (medicalGasInfo != null) medicalGasInfoWidget(), ], ).toShadowContainer(context, borderRadius: 20), ).expanded, FooterActionButton.footerContainer( context: context, - child: AppFilledButton( - buttonColor: AppColor.primary10, - label: context.translation.submitRequest, - onPressed: _submit, - // buttonColor: AppColor.primary10, + child: Row( + children: [ + AppFilledButton( + buttonColor: AppColor.primary10, + label: 'Save'.addTranslation, + onPressed: () { + _update(isSubmitted: false); + }, + // buttonColor: AppColor.primary10, + ).expanded, + 8.width, + AppFilledButton( + buttonColor: AppColor.primary10, + label: 'Submit'.addTranslation, + onPressed: () { + _update(isSubmitted: true); + }, + // buttonColor: AppColor.primary10, + ).expanded, + ], ), ), ], @@ -202,30 +213,87 @@ class _CreateMedicalGasRequestPageState extends State _submit() async { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => MedicalGasInspectionDetailPage(inspectionId: 0))); - return; - FocusScope.of(context).unfocus(); - if (_formKey.currentState!.validate()) { - _formKey.currentState!.save(); + void setGasInfo() async { + if (_medicalGasFormModel.itemType != null && _medicalGasFormModel.site != null && _medicalGasFormModel.supplier != null) { + setState(() { + isGasInfoLoading = true; + }); + medicalGasInfo = + await medicalGasInspectionProvider.getMedicalGasInfo(itemTypeId: _medicalGasFormModel.itemType!.id, supplierId: _medicalGasFormModel.supplier!.id, siteId: _medicalGasFormModel.site!.id); + setState(() { + isGasInfoLoading = false; + }); + } + } - _loanFormModel.loanAttachment = []; - for (var item in attachments) { - String fileName = CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? ''; - _loanFormModel.loanAttachment?.add(LoanAttachments(id: 0, attachmentName: fileName, loanId: 0)); - } - Utils.showLoading(context); - LoanProvider loanProvider = Provider.of(context, listen: false); - Map body = _loanFormModel.toJson(); - body["id"] = 0; - body["LoanTypeId"] = 0; - body["requestorUserID"] = context.userProvider.user!.userID; + Widget medicalGasInfoWidget() { + return isGasInfoLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 8.height, + 'Supplier Contact: ${medicalGasInfo?.supplierContact ?? "-"}'.bodyText(context).custom(color: AppColor.black10), + 8.height, + 'Supplier Email: ${medicalGasInfo?.supplierEmail ?? "-"}'.bodyText(context).custom(color: AppColor.black10), + 8.height, + 'Items'.addTranslation.bodyText(context).custom(color: AppColor.textColor(context)), + 8.height, + // MedicalGasItemsListView( + // itemsList: medicalGasInfo?.attributeInfo ?? [], + // ), + if (medicalGasInfo!.attributeInfo!.isNotEmpty) + ListView.separated( + separatorBuilder: (_, __) => 12.height, + itemCount: medicalGasInfo!.attributeInfo!.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + AttributeInfo model = medicalGasInfo!.attributeInfo![index]; + return AppTextFormField( + labelText: model.itemName, + hintText: 'Enter Quantity'.addTranslation, + backgroundColor: AppColor.fieldBgColor(context), + initialValue: model.requestedQuantity != null ? model.requestedQuantity.toString() : '', + textAlign: TextAlign.center, + enable: true, + textInputType: TextInputType.number, + labelStyle: AppTextStyles.textFieldLabelStyle, + showShadow: false, + onChange: (value) { + if (value.isNotEmpty) { + model.requestedQuantity = int.tryParse(value); + } + }, + style: Theme.of(context).textTheme.titleMedium, + ); + }, + ) + ], + ); + } - bool isSuccess = await loanProvider.addLoanRequest(body); - Utils.hideLoading(context); - if (isSuccess) { - Navigator.pop(context); + Future _update({bool isSubmitted = false}) async { + if (medicalGasInfo != null && medicalGasInfo!.attributeInfo!.isNotEmpty) { + _medicalGasFormModel.gasRequestDetail = []; + for (var item in medicalGasInfo!.attributeInfo!) { + _medicalGasFormModel.gasRequestDetail?.add(MedicalGasFormDetailModel( + id: widget.dataModel != null ? item.id : 0, + medicalGasAttributeId: item.medicalGasAttributeId ?? item.id, + requestedQuantity: item.requestedQuantity, + )); } } + _medicalGasFormModel.isSubmitted = isSubmitted; + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await medicalGasInspectionProvider.saveMedicalGas(model: _medicalGasFormModel).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context, true); + } else { + //This should be handled from backend. + Fluttertoast.showToast(msg: 'No gas available for selected site', toastLength: Toast.LENGTH_LONG); + } + }); } } diff --git a/lib/modules/medical_gas_inspection/medical_gas_inspection_detail_page.dart b/lib/modules/medical_gas_inspection/medical_gas_inspection_detail_page.dart index c34ddfe5..023af466 100644 --- a/lib/modules/medical_gas_inspection/medical_gas_inspection_detail_page.dart +++ b/lib/modules/medical_gas_inspection/medical_gas_inspection_detail_page.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; @@ -9,105 +7,135 @@ 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/cm_module/views/components/action_button/footer_action_button.dart'; -import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_model.dart'; -import 'package:test_sa/modules/internal_audit_module/models/system_internal_audit_data_model.dart'; -import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/update_equipment_internal_audit_page.dart'; -import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/update_system_internal_audit_page.dart'; -import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_provider.dart'; -import 'package:test_sa/modules/loan_module/models/loan_attachment_model.dart'; -import 'package:test_sa/modules/loan_module/models/loan_request_model.dart'; -import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; +import 'package:test_sa/modules/medical_gas_inspection/create_medical_gas_request_page.dart'; +import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_data_model.dart'; +import 'package:test_sa/modules/medical_gas_inspection/providers/medical_gas_inspection_provider.dart'; +import 'package:test_sa/modules/medical_gas_inspection/update_delivery_notes_page.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; import 'package:test_sa/views/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'; -import 'update_medical_gas_inspection_page.dart'; - -class MedicalGasInspectionDetailPage extends StatelessWidget { +class MedicalGasInspectionDetailPage extends StatefulWidget { static const String id = "/medical-gas-inspection-detail-page"; final int inspectionId; + final String? prority; - MedicalGasInspectionDetailPage({Key? key, required this.inspectionId}) : super(key: key); + MedicalGasInspectionDetailPage({Key? key, required this.inspectionId, this.prority}) : super(key: key); + + @override + State createState() => _MedicalGasInspectionDetailPageState(); +} + +class _MedicalGasInspectionDetailPageState extends State { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _loadData(); + }); + } + + MedicalGasDataModel? model; + bool _isLoading = true; + + Future _loadData() async { + final result = await context.read().getMedicalGasById(requestId: widget.inspectionId); + setState(() { + model = result; + _isLoading = false; + }); + } @override Widget build(BuildContext context) { return Scaffold( appBar: const DefaultAppBar(title: "Request Details"), body: SafeArea( - child: FutureBuilder( - future: Provider.of(context, listen: false).getLoanById(0), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) return const CircularProgressIndicator(color: AppColor.primary10).center; - - // if (snapshot.data == null) return const NoDataFound().center; - // - List allAttachments = snapshot.data?.loanAttachments ?? []; - - return Column( - children: [ - ListView( - padding: const EdgeInsets.all(16), + child: _isLoading + ? SizedBox.expand(child: const CircularProgressIndicator(color: AppColor.primary10).center) + : model == null + ? const NoDataFound().center + : Column( children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, + ListView( + padding: const EdgeInsets.all(16), children: [ - // Row( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // StatusLabel( - // label: snapshot.data!.loanStatusName!, - // textColor: AppColor.getRequestStatusTextColorByName(context, snapshot.data!.loanStatusName!), - // backgroundColor: AppColor.getRequestStatusColorByName(context, snapshot.data!.loanStatusName!), - // ), - // 1.width.expanded, - // Text( - // snapshot.data!.createdDate?.toServiceRequestCardFormat ?? "-", - // textAlign: TextAlign.end, - // style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), - // ), - // ], - // ), - Text("Request Details", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), - '${context.translation.requestNo}: ${"model?.requestNo" ?? '-'}'.bodyText(context), - 'Order Type: ${"model?.Order Type" ?? '-'}'.bodyText(context), - 'Item Type: ${"model?.Item Type" ?? '-'}'.bodyText(context), - 'Site: ${"model?.Site" ?? '-'}'.bodyText(context), - 'Cylinders: ${"model?.Cylinders" ?? '-'}'.bodyText(context), - 'LOX: ${"model?.LOX" ?? '-'}'.bodyText(context), - 'Supplier Name: ${"model?.Supplier Name" ?? '-'}'.bodyText(context), - 'Supplier Contact: ${"model?.Supplier Contact" ?? '-'}'.bodyText(context), - 'Supplier Email: ${"model?.Supplier Email" ?? '-'}'.bodyText(context), - - if (allAttachments.isNotEmpty) ...[ - const Divider().defaultStyle(context), - Text( - "Attachments".addTranslation, - style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), - ), - FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []), - ], + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + //Need to return the status form api backend + Row( + children: [ + if (widget.prority != null) ...[ + StatusLabel( + label: widget.prority, + textColor: AppColor.getRequestStatusTextColorByName(context, widget.prority), + backgroundColor: AppColor.getRequestStatusColorByName(context, widget.prority), + ), + 8.width, + ], + StatusLabel( + label: model?.requestStatus?.name, + textColor: AppColor.getHistoryLogStatusTextColorByName(model?.requestStatus?.name ?? ''), + backgroundColor: AppColor.getHistoryLogStatusColorByName(model?.requestStatus?.name ?? ''), + ), + 1.width.expanded, + Text(model!.requestedDate!.toServiceRequestCardFormat, + textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50)) + ], + ), + 8.height, + Text("Request Details", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)), + '${context.translation.requestNo}: ${model?.requestNumber ?? '-'}'.bodyText(context), + 'Requested by: ${model?.requestedBy ?? '-'}'.bodyText(context), + if (model?.lastModifiedBy != null) + 'Last Modified by: ${model?.lastModifiedBy ?? '-'}'.bodyText(context), + 'Order Type: ${model?.requestOrderType ?? '-'}'.bodyText(context), + 'Item Type: ${model?.itemType ?? '-'}'.bodyText(context), + 'Site: ${model?.site ?? '-'}'.bodyText(context), + 'Supplier Name: ${model?.supplier ?? '-'}'.bodyText(context), + 'Supplier Contact: ${model?.contact ?? '-'}'.bodyText(context), + 'Supplier Email: ${model?.email ?? '-'}'.bodyText(context), + if (model!.deliveryNoteAttachmentDto.isNotEmpty) ...[ + const Divider().defaultStyle(context), + Text( + "Attachments".addTranslation, + style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + FilesList(images: model!.deliveryNoteAttachmentDto.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), + ], + ], + ).toShadowContainer(context, padding: 12), ], - ).toShadowContainer(context, padding: 12), + ).expanded, + if (model?.requestStatus?.value != null && model!.requestStatus!.value! < 3) + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + buttonColor: AppColor.primary10, + label: model?.isSubmitted == true ? "Upload Delivery Note" : 'Update', + onPressed: () async { + if (model?.isSubmitted == true) { + bool? isRefresh = await Navigator.of(context).push(MaterialPageRoute(builder: (_) => UpdateDeliveryNotes(dataModel: model))); + if (isRefresh == true) { + _loadData(); + } + return; + } + bool? isRefresh = await Navigator.of(context).push(MaterialPageRoute( + builder: (_) => CreateMedicalGasRequestPage( + dataModel: model, + ))); + if (isRefresh == true) { + _loadData(); + } + }), + ), ], - ).expanded, - if (context.userProvider.isEngineer) - FooterActionButton.footerContainer( - context: context, - child: AppFilledButton( - buttonColor: AppColor.primary10, - label: "Upload Delivery Note", - onPressed: () async { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => UpdateMedicalGasInspectionPage(inspectionId: 0))); - }), - ), - ], - ); - }), + ), )); } @@ -124,20 +152,4 @@ class MedicalGasInspectionDetailPage extends StatelessWidget { ), ); } - - Widget checklistWidget(BuildContext context, {required String value}) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Checkbox( - value: true, - activeColor: AppColor.neutral120, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - visualDensity: const VisualDensity(horizontal: -4, vertical: -3), - onChanged: (value) {}, - ), - value.bodyText(context), - ], - ); - } } diff --git a/lib/modules/medical_gas_inspection/medical_gas_inspection_item_view.dart b/lib/modules/medical_gas_inspection/medical_gas_inspection_item_view.dart new file mode 100644 index 00000000..e03fc500 --- /dev/null +++ b/lib/modules/medical_gas_inspection/medical_gas_inspection_item_view.dart @@ -0,0 +1,130 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/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/all_requests_and_count_model.dart'; +import 'package:test_sa/models/new_models/dashboard_detail.dart'; +import 'package:test_sa/modules/medical_gas_inspection/medical_gas_inspection_detail_page.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; + +class MedicalGasInspectionItemView extends StatelessWidget { + final Data? requestData; + final RequestsDetails? requestDetails; + final bool showShadow; + + const MedicalGasInspectionItemView({Key? key, this.requestData, this.requestDetails, this.showShadow = true}) : super(key: key); + + @override + Widget build(BuildContext context) { + if (requestData != null) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (requestData!.priorityName != null) ...[ + StatusLabel( + label: requestData!.priorityName!, + textColor: AppColor.getRequestStatusTextColorByName(context, requestData!.priorityName!), + backgroundColor: AppColor.getRequestStatusColorByName(context, requestData!.priorityName!), + ), + 8.width, + ], + StatusLabel( + label: requestData!.statusName!, + textColor: AppColor.getHistoryLogStatusTextColorByName(requestData!.statusName!), + backgroundColor: AppColor.getHistoryLogStatusColorByName(requestData!.statusName!), + ), + 1.width.expanded, + Text(requestData!.transactionDate!.toServiceRequestCardFormat, + textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50)), + ], + ), + (requestData?.typeTransaction ?? 'Medical Gas Request').heading5(context), + infoWidget(label: 'Request Number'.addTranslation, value: requestData?.requestNo ?? '-', context: context), + infoWidget(label: 'Request Type'.addTranslation, value: requestData?.requestType ?? '-', context: context), + // infoWidget(label: 'Site'.addTranslation, value: requestData?.si ?? '-', context: context), + // infoWidget(label: 'Gas Type'.addTranslation, value: requestDetails?.gasType ?? '-', context: context), + 8.height, + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.translation.viewDetails, + style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)), + ), + 4.width, + Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14) + ], + ), + ], + ).toShadowContainer(context, withShadow: showShadow).onPress(() async { + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => MedicalGasInspectionDetailPage( + inspectionId: requestData!.id!, + prority: requestData?.priorityName, + ))); + }); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (requestDetails!.priority != null) ...[ + StatusLabel( + label: requestDetails!.priority!, + textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails!.priority!), + backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails!.priority!), + ), + 8.width, + ], + StatusLabel( + label: requestDetails!.status!, + textColor: AppColor.getHistoryLogStatusTextColorByName(requestDetails!.status!), + backgroundColor: AppColor.getHistoryLogStatusColorByName(requestDetails!.status!), + ), + 1.width.expanded, + Text(requestDetails!.date!.toServiceRequestCardFormat, textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50)), + ], + ), + (requestDetails?.nameOfType ?? 'Medical Gas Request').heading5(context), + infoWidget(label: 'Request Number'.addTranslation, value: requestDetails?.requestNo ?? '-', context: context), + infoWidget(label: 'Request Type'.addTranslation, value: requestDetails?.requestType ?? '-', context: context), + infoWidget(label: 'Site'.addTranslation, value: requestDetails?.site ?? '-', context: context), + 8.height, + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.translation.viewDetails, + style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)), + ), + 4.width, + Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14) + ], + ), + ], + ).toShadowContainer(context, withShadow: showShadow).onPress(() async { + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => MedicalGasInspectionDetailPage( + inspectionId: requestDetails!.id!, + prority: requestDetails?.priority, + ))); + }); + } + + Widget infoWidget({required String label, String? value, required BuildContext context}) { + if (value != null && value.isNotEmpty) { + return '$label: $value'.bodyText(context); + } + return const SizedBox(); + } +} diff --git a/lib/modules/medical_gas_inspection/medical_gas_item_list_view.dart b/lib/modules/medical_gas_inspection/medical_gas_item_list_view.dart new file mode 100644 index 00000000..7b399233 --- /dev/null +++ b/lib/modules/medical_gas_inspection/medical_gas_item_list_view.dart @@ -0,0 +1,94 @@ +// 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/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/models/cost_center_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'; +// import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_supplier_model.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_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'; +// +// class MedicalGasItemsListView extends StatelessWidget { +// final List itemsList; +// +// MedicalGasItemsListView({ +// super.key, +// required this.itemsList, +// }); +// +// late AssetDeliveryProvider assetDeliveryProvider; +// +// @override +// Widget build(BuildContext context) { +// // return ListView.separated( +// // separatorBuilder: (_, __) => 12.height, +// // itemCount: itemsList.length, +// // shrinkWrap: true, +// // itemBuilder: (context, index) { +// // AttributeInfo model = itemsList[index]; +// // return AppTextFormField( +// // labelText: model.itemName, +// // hintText: 'Enter Quantity'.addTranslation, +// // backgroundColor: AppColor.fieldBgColor(context), +// // initialValue: model.requestedQuantity != null ? model.requestedQuantity.toString() : '', +// // textAlign: TextAlign.center, +// // enable: true, +// // textInputType: TextInputType.number, +// // labelStyle: AppTextStyles.textFieldLabelStyle, +// // showShadow: false, +// // onChange: (value) { +// // if (value.isNotEmpty) { +// // model.requestedQuantity = int.tryParse(value); +// // } +// // }, +// // style: Theme.of(context).textTheme.titleMedium, +// // ); +// // }, +// // ); +// } +// } +// +// // class ItemCard extends StatelessWidget { +// // final AttributeInfo model; +// // final int index; +// // +// // const ItemCard({ +// // super.key, +// // required this.model, +// // required this.index, +// // }); +// // +// // @override +// // Widget build(BuildContext context) { +// // return AppTextFormField( +// // labelText: model.itemName, +// // hintText: 'Enter Quantity'.addTranslation, +// // backgroundColor: AppColor.fieldBgColor(context), +// // initialValue: model.requestedQuantity != null ? model.requestedQuantity.toString() : '', +// // textAlign: TextAlign.center, +// // enable: true, +// // textInputType: TextInputType.number, +// // labelStyle: AppTextStyles.textFieldLabelStyle, +// // showShadow: false, +// // onChange: (value) { +// // if (value.isNotEmpty) { +// // model.requestedQuantity = int.tryParse(value); +// // } +// // }, +// // style: Theme.of(context).textTheme.titleMedium, +// // ); +// // } +// // } diff --git a/lib/modules/medical_gas_inspection/models/delivery_notes_form_model.dart b/lib/modules/medical_gas_inspection/models/delivery_notes_form_model.dart new file mode 100644 index 00000000..5b86859c --- /dev/null +++ b/lib/modules/medical_gas_inspection/models/delivery_notes_form_model.dart @@ -0,0 +1,92 @@ +import 'package:test_sa/models/generic_attachment_model.dart'; +import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_data_model.dart'; + +class DeliveryNotesFormModel { + int? id; + DateTime? deliveredDate; + String? deliveryNoteNumber; + bool? isPressureChecked; + bool? isInspectedGasLeak; + bool? isSubmitted; + + List gasDeliveredQuantityDtos = []; + List deliveredItemList = []; + List attachments = []; + + DeliveryNotesFormModel({ + this.id, + this.deliveredDate, + this.deliveryNoteNumber, + this.isPressureChecked, + this.isInspectedGasLeak, + this.isSubmitted, + List? gasDeliveredQuantityDtos, + List? deliveredItemList, + List? attachments, + }) { + this.gasDeliveredQuantityDtos = gasDeliveredQuantityDtos ?? []; + this.deliveredItemList = deliveredItemList ?? []; + this.attachments = attachments ?? []; + } + + DeliveryNotesFormModel.fromJson(Map json) { + id = json['id']; + deliveredDate = json['deliveredDate'] != null ? DateTime.parse(json['deliveredDate']) : null; + deliveryNoteNumber = json['deliveryNoteNumber']; + isPressureChecked = json['isPressureChecked']; + isInspectedGasLeak = json['isInspectedGasLeak']; + isSubmitted = json['isSubmitted']; + if (json['gasDeliveredQuantityDtos'] != null) { + gasDeliveredQuantityDtos = []; + json['gasDeliveredQuantityDtos'].forEach((v) { + gasDeliveredQuantityDtos.add(GasDeliveredQuantityModel.fromJson(v)); + }); + } + + if (json['cUMedicalGasDeliveryNoteAttachmentDtos'] != null) { + attachments = []; + json['cUMedicalGasDeliveryNoteAttachmentDtos'].forEach((v) { + attachments.add(GenericAttachmentModel.fromJson(v)); + }); + } + } + + Map toJson() { + return { + 'id': id, + 'deliveredDate': deliveredDate?.toIso8601String(), + 'deliveryNoteNumber': deliveryNoteNumber, + 'isPressureChecked': isPressureChecked, + 'isInspectedGasLeak': isInspectedGasLeak, + 'isSubmitted': isSubmitted, + 'gasDeliveredQuantityDtos': gasDeliveredQuantityDtos.map((e) => e.toJson()).toList(), + 'cUMedicalGasDeliveryNoteAttachmentDtos': attachments.map((e) => e.toJson()).toList(), + }; + } +} + +class GasDeliveredQuantityModel { + int? id; + num? medicalGasAttributeId; + num? deliveredQuantity; + + GasDeliveredQuantityModel({ + this.id, + this.medicalGasAttributeId, + this.deliveredQuantity, + }); + + GasDeliveredQuantityModel.fromJson(Map json) { + id = json['id']; + medicalGasAttributeId = json['medicalGasAttributeId']; + deliveredQuantity = json['deliveredQuantity']; + } + + Map toJson() { + return { + 'id': id, + 'medicalGasAttributeId': medicalGasAttributeId, + 'deliveredQuantity': deliveredQuantity, + }; + } +} diff --git a/lib/modules/medical_gas_inspection/models/medical_gas_data_model.dart b/lib/modules/medical_gas_inspection/models/medical_gas_data_model.dart new file mode 100644 index 00000000..8c784b78 --- /dev/null +++ b/lib/modules/medical_gas_inspection/models/medical_gas_data_model.dart @@ -0,0 +1,174 @@ +import 'package:test_sa/models/base.dart'; +import 'package:test_sa/models/generic_attachment_model.dart'; +import 'package:test_sa/models/lookup.dart'; + +class MedicalGasDataModel { + int? id; + String? requestNumber; + String? requestedBy; + String? requestedDate; + int? requestOrderTypeId; + String? requestOrderType; + int? itemTypeId; + String? itemType; + int? siteId; + String? site; + int? supplierId; + String? supplier; + String? contact; + String? email; + bool? isSubmitted; + String? requestOrderStatus; + bool? isPressureChecked; + bool? isInspectedGasLeak; + Lookup? requestStatus; + DateTime? deliveredDate; + String? deliveryNote; + String? lastModifiedBy; + + /// ✅ non-nullable lists + List requestDetailDtos = []; + List deliveryNoteAttachmentDto = []; + + MedicalGasDataModel({ + this.id, + this.requestNumber, + this.requestedBy, + this.requestedDate, + this.requestOrderTypeId, + this.requestOrderType, + this.itemTypeId, + this.itemType, + this.siteId, + this.site, + this.requestStatus, + this.supplierId, + this.supplier, + this.contact, + this.email, + this.isSubmitted, + this.requestOrderStatus, + this.isPressureChecked, + this.isInspectedGasLeak, + this.deliveredDate, + this.deliveryNote, + this.lastModifiedBy, + List? requestDetailDtos, + List? deliveryNoteAttachmentDto, + }) { + this.requestDetailDtos = requestDetailDtos ?? []; + this.deliveryNoteAttachmentDto = deliveryNoteAttachmentDto ?? []; + } + + MedicalGasDataModel.fromJson(Map json) { + id = json['id']; + requestNumber = json['requestNumber']; + requestedBy = json['requestedBy']; + requestedDate = json['requestedDate']; + requestOrderTypeId = json['requestOrderTypeId']; + requestOrderType = json['requestOrderType']; + itemTypeId = json['itemTypeId']; + itemType = json['itemType']; + siteId = json['siteId']; + site = json['site']; + supplierId = json['supplierId']; + supplier = json['supplier']; + contact = json['contact']; + email = json['email']; + isSubmitted = json['isSubmitted']; + deliveryNote = json['deliveryNote']; + lastModifiedBy = json['lastModifiedBy']; + requestOrderStatus = json['requestOrderStatus']; + isPressureChecked = json['isPressureChecked']; + isInspectedGasLeak = json['isInspectedGasLeak']; + + deliveredDate = json['deliveredDate'] != null ? DateTime.parse(json['deliveredDate']) : null; + requestStatus = json['requestOrderStatusDto'] != null ? Lookup.fromJson(json['requestOrderStatusDto']) : null; + if (json['requestDetailDtos'] != null) { + requestDetailDtos = []; + json['requestDetailDtos'].forEach((v) { + requestDetailDtos.add(MedicalGasRequestDetailModel.fromJson(v)); + }); + } + + if (json['deliveryNoteAttachmentDto'] != null) { + deliveryNoteAttachmentDto = []; + json['deliveryNoteAttachmentDto'].forEach((v) { + deliveryNoteAttachmentDto.add(GenericAttachmentModel.fromJson(v)); + }); + } + } + + Map toJson() { + return { + 'id': id, + 'requestNumber': requestNumber, + 'requestedBy': requestedBy, + 'requestedDate': requestedDate, + 'requestOrderTypeId': requestOrderTypeId, + 'requestOrderType': requestOrderType, + 'itemTypeId': itemTypeId, + 'itemType': itemType, + 'siteId': siteId, + 'site': site, + 'supplierId': supplierId, + 'deliveryNote': deliveryNote, + 'lastModifiedBy': lastModifiedBy, + 'supplier': supplier, + 'contact': contact, + 'email': email, + 'isSubmitted': isSubmitted, + 'requestOrderStatus': requestOrderStatus, + 'isPressureChecked': isPressureChecked, + 'isInspectedGasLeak': isInspectedGasLeak, + 'deliveredDate': deliveredDate?.toIso8601String(), + 'requestDetailDtos': requestDetailDtos.map((e) => e.toJson()).toList(), + 'requestOrderStatusDto': requestStatus?.toJson(), + 'deliveryNoteAttachmentDto': deliveryNoteAttachmentDto.map((e) => e.toJson()).toList(), + }; + } +} + +class MedicalGasRequestDetailModel extends Base { + int? id; + int? medicalGasAttributeId; + String? itemName; + String? oracleName; + String? oracleCode; + num? requestedQuantity; + num? deliveredQuantity; + + MedicalGasRequestDetailModel({ + this.id, + this.medicalGasAttributeId, + this.itemName, + this.oracleName, + this.oracleCode, + this.requestedQuantity, + this.deliveredQuantity, + }) : super(identifier: id?.toString(), name: itemName); + + MedicalGasRequestDetailModel.fromJson(Map json) { + id = json['id']; + medicalGasAttributeId = json['medicalGasAttributeId']; + itemName = json['itemName']; + identifier = id.toString(); + name = itemName; + oracleName = json['oracleName']; + oracleCode = json['oracleCode']; + requestedQuantity = json['requestedQuantity']; + deliveredQuantity = json['deliveredQuantity']; + } + + Map toJson() { + return { + 'id': id, + 'medicalGasAttributeId': medicalGasAttributeId, + 'itemName': itemName, + 'oracleName': oracleName, + 'oracleCode': oracleCode, + 'requestedQuantity': requestedQuantity, + 'deliveredQuantity': deliveredQuantity, + }; + } +} diff --git a/lib/modules/medical_gas_inspection/models/medical_gas_form_model.dart b/lib/modules/medical_gas_inspection/models/medical_gas_form_model.dart new file mode 100644 index 00000000..a3f08ac1 --- /dev/null +++ b/lib/modules/medical_gas_inspection/models/medical_gas_form_model.dart @@ -0,0 +1,87 @@ +import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/models/new_models/site.dart'; +import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; +import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart'; + +class MedicalGasFormModel { + int? id; + int? requestOrderTypeId; + Lookup? orderType; + Lookup? itemType; + int? itemTypeId; + int? siteId; + Site? site; + MedicalDepartmentModel? department; + MedicalGasSupplierModel? supplier; + int? supplierId; + bool? isSubmitted; + List? gasRequestDetail; + + MedicalGasFormModel({ + this.id, + this.requestOrderTypeId, + this.itemTypeId, + this.siteId, + this.supplierId, + this.supplier, + this.isSubmitted, + this.orderType, + this.itemType, + this.site, + this.department, + this.gasRequestDetail, + }); + + MedicalGasFormModel.fromJson(Map json) { + id = json['id']; + requestOrderTypeId = json['requestOrderTypeId']; + itemTypeId = json['itemTypeId']; + siteId = json['siteId']; + supplierId = json['supplierId']; + isSubmitted = json['isSubmitted']; + if (json['gasRequestDetailDtos'] != null) { + gasRequestDetail = []; + json['gasRequestDetailDtos'].forEach((v) { + gasRequestDetail!.add(MedicalGasFormDetailModel.fromJson(v)); + }); + } + } + + Map toJson() { + return { + 'id': id ?? 0, + 'requestOrderTypeId': orderType?.id, + 'itemTypeId': itemType?.id, + 'siteId': site?.id, + 'supplierId': supplier?.id, + 'isSubmitted': isSubmitted, + 'gasRequestDetailDtos': gasRequestDetail?.map((e) => e.toJson()).toList(), + }; + } +} + +class MedicalGasFormDetailModel { + int? id; + int? medicalGasAttributeId; + num? requestedQuantity; + + MedicalGasFormDetailModel({ + this.id, + this.medicalGasAttributeId, + this.requestedQuantity, + }); + + MedicalGasFormDetailModel.fromJson(Map json) { + id = json['id']; + medicalGasAttributeId = json['medicalGasAttributeId']; + requestedQuantity = json['requestedQuantity']; + } + + Map toJson() { + return { + 'id': id, + 'medicalGasAttributeId': medicalGasAttributeId, + 'requestedQuantity': requestedQuantity??0, + }; + } +} diff --git a/lib/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart b/lib/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart new file mode 100644 index 00000000..17ca804f --- /dev/null +++ b/lib/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart @@ -0,0 +1,72 @@ + + +import 'package:test_sa/models/base.dart'; + +class MedicalGasSupplierModel extends Base { + int? id; + String? supplier; + String? supplierEmail; + String? supplierContact; + List? attributeInfo; + + MedicalGasSupplierModel({this.id, this.supplier, this.supplierContact, this.supplierEmail, this.attributeInfo}) : super(identifier: id?.toString() ?? '', name: supplier); + + MedicalGasSupplierModel.fromJson(Map json) { + id = json['id']; + identifier = id?.toString() ?? ''; + supplier = json['supplier']; + name=supplier; + supplierEmail = json['supplierEmail']; + supplierContact = json['supplierContact']; + if (json['attributeInfoDtos'] != null) { + attributeInfo = []; + json['attributeInfoDtos'].forEach((v) { + attributeInfo!.add(AttributeInfo.fromJson(v)); + }); + } + } + + Map toJson() { + return { + 'id': id, + 'supplier': supplier, + 'supplierEmail': supplierEmail, + 'supplierContact': supplierContact, + 'attributeInfoDtos': attributeInfo?.map((e) => e.toJson()).toList(), + }; + } +} + +class AttributeInfo { + int? id; + int? medicalGasAttributeId; + num? requestedQuantity; + String? itemName; + String? oracleName; + String? oracleCode; + + AttributeInfo({this.id, this.medicalGasAttributeId, this.requestedQuantity, this.itemName, this.oracleName, this.oracleCode}); + + AttributeInfo.fromJson(Map json) { + id = json['id']; + medicalGasAttributeId = json['medicalGasAttributeId']; + requestedQuantity = json['requestedQuantity']; + itemName = json['itemName']; + oracleName = json['oracleName']; + oracleCode = json['oracleCode']; + } + + Map toJson() { + //{ + // "id": 0, + // "medicalGasAttributeId": 0, + // "requestedQuantity": 0 + // } + return { + 'id': id, + 'medicalGasAttributeId': medicalGasAttributeId, + 'requestedQuantity': requestedQuantity, + // 'oracleCode': oracleCode, + }; + } +} diff --git a/lib/modules/medical_gas_inspection/providers/item_type_provider.dart b/lib/modules/medical_gas_inspection/providers/item_type_provider.dart new file mode 100644 index 00000000..e7c38208 --- /dev/null +++ b/lib/modules/medical_gas_inspection/providers/item_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 MedicalGasItemTypeProvider 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.itemTypeLookUp); + 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/medical_gas_inspection/providers/medical_gas_inspection_provider.dart b/lib/modules/medical_gas_inspection/providers/medical_gas_inspection_provider.dart new file mode 100644 index 00000000..a28c76ee --- /dev/null +++ b/lib/modules/medical_gas_inspection/providers/medical_gas_inspection_provider.dart @@ -0,0 +1,119 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'package:flutter/material.dart'; +import 'package:test_sa/controllers/api_routes/api_manager.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/modules/medical_gas_inspection/models/delivery_notes_form_model.dart'; +import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_data_model.dart'; +import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_form_model.dart'; +import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart'; + +class MedicalGasInspectionProvider extends ChangeNotifier { + final pageItemNumber = 10; + final searchPageItemNumber = 10; + int pageNo = 1; + + void reset() { + pageNo = 1; + stateCode = null; + } + + int? stateCode; + bool isDetailLoading = false; + bool nextPage = false; + bool isLoading = false; + bool isGasInfoLoading = false; + + Future getMedicalGasById({int? requestId}) async { + MedicalGasDataModel? assetDeliveryDataModel = MedicalGasDataModel(); + + try { + isLoading = true; + notifyListeners(); + final response = await ApiManager.instance.get(URLs.getMedicalGasById + "?medicalGasRequestId=$requestId"); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + assetDeliveryDataModel = MedicalGasDataModel.fromJson(json.decode(response.body)["data"]); + } else { + assetDeliveryDataModel = null; + } + isLoading = false; + notifyListeners(); + return assetDeliveryDataModel; + } catch (e) { + log("getMedicalGas details [error] : $e"); + isLoading = false; + assetDeliveryDataModel = null; + notifyListeners(); + return assetDeliveryDataModel; + } + } + + Future saveMedicalGas({ + required MedicalGasFormModel model, + }) async { + isLoading = true; + try { + final response = await ApiManager.instance.post(URLs.addMedicalGasRequest, body: model.toJson()); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return false; + } + } + + Future getMedicalGasInfo({int? supplierId, int? itemTypeId, num? siteId}) async { + MedicalGasSupplierModel? supplierInfo; + Map payload = {"supplierId": supplierId, "itemTypeId": itemTypeId, "siteId": siteId}; + try { + isGasInfoLoading = true; + notifyListeners(); + final response = await ApiManager.instance.post("${URLs.getMedicalGasInfoBySupplier}", body: payload); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + final jsonData = json.decode(response.body)["data"]; + supplierInfo = MedicalGasSupplierModel.fromJson(jsonData); + } + isGasInfoLoading = false; + notifyListeners(); + return supplierInfo; + } catch (e) { + log("get Supplier List [error] : $e"); + isGasInfoLoading = false; + notifyListeners(); + return supplierInfo; + } + } + Future updatedDeliveryNotes({ + required DeliveryNotesFormModel model, + }) async { + isLoading = true; + try { + final response = await ApiManager.instance.post(URLs.updateDeliveryNote, body: model.toJson()); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return false; + } + } +} diff --git a/lib/modules/medical_gas_inspection/providers/medical_gas_supplier_provider.dart b/lib/modules/medical_gas_inspection/providers/medical_gas_supplier_provider.dart new file mode 100644 index 00000000..d7127726 --- /dev/null +++ b/lib/modules/medical_gas_inspection/providers/medical_gas_supplier_provider.dart @@ -0,0 +1,34 @@ +import 'dart:convert'; +import 'dart:developer'; + +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/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; + +class MedicalGasSupplierProvider extends LoadingListNotifier { + @override + Future getData({int? id}) async { + loading = true; + notifyListeners(); + Response response; + try { + response = await ApiManager.instance.get(URLs.getMedicalGasSupplierList); + } catch (error) { + loading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + List listJson = json.decode(response.body)["data"]; + items = listJson.map((supplier) => MedicalGasSupplierModel.fromJson(supplier)).toList(); + } + loading = false; + notifyListeners(); + return response.statusCode; + } +} diff --git a/lib/modules/medical_gas_inspection/providers/order_type_provider.dart b/lib/modules/medical_gas_inspection/providers/order_type_provider.dart new file mode 100644 index 00000000..32d19716 --- /dev/null +++ b/lib/modules/medical_gas_inspection/providers/order_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 MedicalGasOrderTypeProvider 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.orderTypeLookUp); + 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/medical_gas_inspection/update_delivery_notes_page.dart b/lib/modules/medical_gas_inspection/update_delivery_notes_page.dart new file mode 100644 index 00000000..925f5193 --- /dev/null +++ b/lib/modules/medical_gas_inspection/update_delivery_notes_page.dart @@ -0,0 +1,384 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/all_requests_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/modules/cm_module/views/components/action_button/footer_action_button.dart'; +import 'package:test_sa/modules/medical_gas_inspection/models/delivery_notes_form_model.dart'; +import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_data_model.dart'; +import 'package:test_sa/modules/medical_gas_inspection/providers/medical_gas_inspection_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_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/multiple_item_drop_down_menu.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; +import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; +import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; + +class UpdateDeliveryNotes extends StatefulWidget { + final MedicalGasDataModel? dataModel; + + UpdateDeliveryNotes({Key? key, required this.dataModel}) : super(key: key); + + @override + _UpdateDeliveryNotesState createState() { + return _UpdateDeliveryNotesState(); + } +} + +class _UpdateDeliveryNotesState extends State { + DeliveryNotesFormModel formModel = DeliveryNotesFormModel(); + late MedicalGasInspectionProvider medicalGasInspectionProvider; + bool pressureTestAcknowledgement = false; + bool leakTestAcknowledgement = false; + + // List selectedItemList = []; + + @override + void initState() { + super.initState(); + medicalGasInspectionProvider = Provider.of(context, listen: false); + _populateFormData(); + } + + @override + void dispose() { + super.dispose(); + } + + _populateFormData() { + if (widget.dataModel != null) { + MedicalGasDataModel dataModel = widget.dataModel!; + formModel = DeliveryNotesFormModel( + id: dataModel.id, + deliveredItemList: dataModel.requestDetailDtos, + isInspectedGasLeak: dataModel.isInspectedGasLeak, + isPressureChecked: dataModel.isPressureChecked, + deliveredDate: dataModel.deliveredDate, + attachments: dataModel.deliveryNoteAttachmentDto, + isSubmitted: dataModel.isSubmitted, + deliveryNoteNumber: dataModel.deliveryNote); + //Need to Confirm this condition + // selectedItemList = dataModel.requestDetailDtos.where((item) => item.deliveredQuantity != null && item.deliveredQuantity! > 0).toList(); + setState(() {}); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const DefaultAppBar( + title: "Upload Delivery Note", + // onWillPopScope: () { + // _update(context: context); + // }, + ), + body: SafeArea( + child: Column( + children: [ + ListView( + padding: const EdgeInsets.all(16), + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ADatePicker( + label: "Delivered Date", + hideShadow: true, + backgroundColor: AppColor.fieldBgColor(context), + from: DateTime.now().subtract(const Duration(days: 365 * 3)), + date: formModel.deliveredDate, + formatDateWithTime: true, + onDatePicker: (selectedDate) { + showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ).then((selectedTime) { + if (selectedTime != null) { + formModel.deliveredDate = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute); + setState(() {}); + } + }); + }, + ), + 16.height, + AppTextFormField( + initialValue: formModel.deliveryNoteNumber, + labelText: "Delivery Notes", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + floatingLabelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.multiline, + makeMultiLinesNull: true, + onChange: (value) { + formModel.deliveryNoteNumber = value; + }, + ), + 16.height, + + //Older design code + // MultipleItemDropDownMenu( + // context: context, + // showAsBottomSheet: true, + // backgroundColor: AppColor.neutral100, + // showShadow: false, + // showCancel: true, + // requestById: context.userProvider.user?.clientId, + // title: 'Delivered Items'.addTranslation, + // staticData: formModel.deliveredItemList, + // initialValue: selectedItemList, + // onSelect: (value) { + // if ((value ?? []).isNotEmpty) { + // WidgetsBinding.instance.addPostFrameCallback((_) { + // if (!mounted) return; + // setState(() { + // selectedItemList = value ?? []; + // }); + // }); + // } + // }, + // ), + // if (selectedItemList.isNotEmpty) ...[ + // 8.height, + // ListView.separated( + // separatorBuilder: (_, __) => 12.height, + // itemCount: selectedItemList.length, + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // itemBuilder: (context, index) { + // MedicalGasRequestDetailModel model = selectedItemList[index]; + // return AppTextFormField( + // labelText: '${model.name} (Req Qty =${model.requestedQuantity})', + // hintText: 'Enter Delivered Quantity'.addTranslation, + // backgroundColor: AppColor.fieldBgColor(context), + // initialValue: model.deliveredQuantity != null ? model.deliveredQuantity?.toInt().toString() : '', + // textAlign: TextAlign.center, + // enable: true, + // textInputType: TextInputType.number, + // labelStyle: AppTextStyles.textFieldLabelStyle, + // showShadow: false, + // onChange: (value) { + // if (value.isNotEmpty) { + // model.deliveredQuantity = double.tryParse(value); + // } + // }, + // style: Theme.of(context).textTheme.titleMedium, + // ); + // }, + // ) + // ], + if (formModel.deliveredItemList.isNotEmpty) ...[ + _ItemRowLayout( + left: 'Items'.addTranslation.bodyText(context).custom(color: AppColor.textColor(context)), + right: 'Delivered Qty'.addTranslation.bodyText(context).custom(color: AppColor.textColor(context)), + ), + 8.height, + ListView.separated( + separatorBuilder: (_, __) => 12.height, + itemCount: formModel.deliveredItemList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + MedicalGasRequestDetailModel model = formModel.deliveredItemList[index]; + return itemQuantityFieldWidget(index: index, model: model, context: context); + }, + ) + ], + 16.height, + + AttachmentPicker( + label: 'Upload Attachment', + attachment: formModel.attachments, + buttonColor: AppColor.primary10, + // showAsListView: true, + onlyImages: false, + buttonIcon: 'attachment_icon'.toSvgAsset( + color: AppColor.primary10, + ), + ), + 16.height, + //should create a reusable widget. + Row( + children: [ + Checkbox( + value: formModel.isPressureChecked, + visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0), + activeColor: AppColor.blueStatus(context), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + onChanged: (value) { + setState(() { + formModel.isPressureChecked = value!; + }); + }), + 12.width, + "I Acknowledge that the received medical gases cylinders have been tested for pressure." + .addTranslation + .bodyText(context) + .custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral120) + .expanded, + ], + ), + 8.height, + Row( + // mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Checkbox( + value: formModel.isInspectedGasLeak, + visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0), + activeColor: AppColor.blueStatus(context), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + onChanged: (value) { + setState(() { + formModel.isInspectedGasLeak = value!; + }); + }), + 12.width, + "I Acknowledge that the received medical gases cylinders have been inspected for leak test." + .addTranslation + .bodyText(context) + .custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral120) + .expanded, + ], + ), + ], + ).toShadowContainer(context, padding: 12), + ], + ).expanded, + FooterActionButton.footerContainer( + context: context, + child: Row( + children: [ + AppFilledButton( + buttonColor: AppColor.primary10, + label: 'Save'.addTranslation, + onPressed: () { + _update(context: context); + }, + // buttonColor: AppColor.primary10, + ).expanded, + 8.width, + AppFilledButton( + buttonColor: AppColor.primary10, + label: 'Submit'.addTranslation, + onPressed: () { + _update(context: context, isSubmitted: true); + }, + // buttonColor: AppColor.primary10, + ).expanded, + ], + ), + ), + ], + ), + )); + // .handlePopScope( + // cxt: context, + // onSave: () { + // _update(context: context); + // }); + } + + void _update({required BuildContext context, bool isSubmitted = false}) async { + formModel.gasDeliveredQuantityDtos = []; + formModel.isSubmitted = isSubmitted; + for (var item in formModel.deliveredItemList) { + formModel.gasDeliveredQuantityDtos.add(GasDeliveredQuantityModel( + id: widget.dataModel != null ? item.id : 0, + medicalGasAttributeId: item.medicalGasAttributeId ?? item.id, + deliveredQuantity: item.deliveredQuantity, + )); + } + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await medicalGasInspectionProvider.updatedDeliveryNotes(model: formModel).then((status) async { + Navigator.pop(context); + if (status) { + Navigator.pop(context, true); + if (formModel.isSubmitted == true) { + AllRequestsProvider allRequestsProvider = Provider.of(context, listen: false); + allRequestsProvider.reset(); + await allRequestsProvider.getAllRequests(context, typeTransaction: 14); + } + } + }); + } + + Widget itemQuantityFieldWidget({ + required int index, + required MedicalGasRequestDetailModel model, + required BuildContext context, + }) { + final border = OutlineInputBorder( + borderRadius: BorderRadius.circular(4), + borderSide: const BorderSide(color: AppColor.white70, width: 0), + ); + + return _ItemRowLayout( + left: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + model.itemName!.bodyText2(context).custom( + color: context.isDark ? Colors.white : AppColor.white936, + fontWeight: FontWeight.w500, + ), + 'Req Qty ${model.requestedQuantity}'.bodyText2(context).custom( + color: context.isDark ? Colors.white : AppColor.white936, + fontWeight: FontWeight.w500, + ), + ], + ), + right: TextFormField( + keyboardType: TextInputType.number, + initialValue: model.deliveredQuantity != null ? model.deliveredQuantity?.toStringAsFixed(0) : '', + decoration: InputDecoration( + contentPadding: EdgeInsets.symmetric(horizontal: 5.toScreenWidth), + filled: true, + fillColor: AppColor.fieldBgColor(context), + constraints: BoxConstraints( + maxHeight: 30.toScreenHeight, + ), + border: border, + enabledBorder: border, + focusedBorder: border, + ), + onChanged: (value) { + model.deliveredQuantity = int.tryParse(value); + }, + ), + ); + } +} + +class _ItemRowLayout extends StatelessWidget { + final Widget left; + final Widget right; + + const _ItemRowLayout({ + required this.left, + required this.right, + }); + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded(child: left), // Items column + SizedBox( + width: 100.toScreenWidth, // Delivered Qty column + child: right, + ), + ], + ); + } +} diff --git a/lib/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart b/lib/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart index fccf0af5..43601cec 100644 --- a/lib/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart +++ b/lib/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart @@ -69,7 +69,7 @@ class _RecurrentWorkOrderViewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ RecurrentTaskInfoWidget(model: requestProvider.recurrentWoData), - requestProvider.recurrentWoData!.planRecurrentMedicalTaskRooms!.isNotEmpty + requestProvider.recurrentWoData!.planRecurrentMedicalTaskRooms != null && requestProvider.recurrentWoData!.planRecurrentMedicalTaskRooms!.isNotEmpty ? Column( children: [ 8.height, 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 e249bd35..6e843398 100644 --- a/lib/modules/tm_module/device_transfer/device_transfer_details.dart +++ b/lib/modules/tm_module/device_transfer/device_transfer_details.dart @@ -223,11 +223,11 @@ class _DeviceTransferDetailsState extends State { if (context.userProvider.isEngineer || context.userProvider.isNurse) ChatWidget( moduleId: widget.moduleId, - isShow: _model!.senderMachineStatusValue! != 4, + isShow: context.settingProvider.isUserFlowMedical && _model!.senderMachineStatusValue! != 4, isReadOnly: _model!.senderMachineStatusValue! != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: _model!.senderAssignedEmployeeNumber, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: _model!.assetTransferContactPersons!.first.employeeNumber, ), if ((_userProvider!.user?.type == UsersTypes.engineer)) @@ -284,11 +284,11 @@ class _DeviceTransferDetailsState extends State { if (context.userProvider.isEngineer || context.userProvider.isNurse) ChatWidget( moduleId: widget.moduleId, - isShow: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue!) != 0, + isShow: context.settingProvider.isUserFlowMedical && (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue!) != 0, isReadOnly: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue) != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: isSender ? _deviceTransfer.senderAssignedEmployeeNumber! : _deviceTransfer.receiverAssignedEmployeeNumber!, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: _deviceTransfer.assetTransferContactPersons!.first.employeeNumber, ), if ((_userProvider!.user?.type == UsersTypes.engineer)) diff --git a/lib/modules/tm_module/gas_refill/gas_refill_details.dart b/lib/modules/tm_module/gas_refill/gas_refill_details.dart index 00ac17aa..c785a6a1 100644 --- a/lib/modules/tm_module/gas_refill/gas_refill_details.dart +++ b/lib/modules/tm_module/gas_refill/gas_refill_details.dart @@ -68,11 +68,11 @@ class _GasRefillDetailsPageState extends State { return ChatWidget( moduleId: widget.moduleId, - isShow: _gasRefillModel.status!.value! != 0, + isShow: context.settingProvider.isUserFlowMedical && _gasRefillModel.status!.value! != 0, isReadOnly: _gasRefillModel.status!.value! != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: _gasRefillModel.assignedEmployee?.employeeId!, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: _gasRefillModel.gasRefillContactPerson!.first.employeeCode!, ); }) 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 91a75fc7..1a60c0b9 100644 --- a/lib/modules/tm_module/tasks/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks/task_request_detail_view.dart @@ -75,16 +75,20 @@ class _TaskRequestDetailsViewState extends State { selector: (_, myModel) => myModel.taskRequestModel, // Selects only the userName builder: (_, _taskReqModel, __) { if (_taskReqModel == null) return const SizedBox(); - - return ChatWidget( - moduleId: widget.moduleId, - isShow: _taskReqModel.taskJobStatus!.value! != 1, - isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, - requestId: widget.taskId.toInt(), - assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!, - myLoginUserID: context.userProvider.user!.username!, - contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId, - ); + if ((context.userProvider.isEngineer || context.userProvider.isNurse) && + _taskReqModel.assignedEngineer?.employeeId != null && + (_taskReqModel.taskJobContactPersons?.isNotEmpty ?? false)) { + return ChatWidget( + moduleId: widget.moduleId, + isShow: 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(); }) ], ), diff --git a/lib/new_views/app_style/app_color.dart b/lib/new_views/app_style/app_color.dart index 8adc321d..8f2ab1c6 100644 --- a/lib/new_views/app_style/app_color.dart +++ b/lib/new_views/app_style/app_color.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:test_sa/extensions/context_extension.dart'; @@ -119,6 +121,8 @@ class AppColor { return orange60; case "high priority": return white10; + case "emergency": + return white10; case "yes": return white10; case "low priority": @@ -134,6 +138,8 @@ class AppColor { switch ((status).toLowerCase()) { case "in progress": return white936; + case "waiting for delivery": + return white936; case "new": return primary63; case "open": @@ -193,6 +199,14 @@ class AppColor { return red30; case "returned": return red30; + case "waiting for delivery": + return orange30; + case "waiting for medical gas specialist review": + return orange30; + case "reviewed": + return orange30; + case "returned for correction": + return orange30; default: return Colors.white; } @@ -210,6 +224,8 @@ class AppColor { return greenStatus(context); case "high priority": return redStatus(context); + case "emergency": + return redStatus(context); case "yes": return redStatus(context); case "low priority": 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/common_widgets/default_app_bar.dart b/lib/new_views/common_widgets/default_app_bar.dart index faaca183..3b3fcf3f 100644 --- a/lib/new_views/common_widgets/default_app_bar.dart +++ b/lib/new_views/common_widgets/default_app_bar.dart @@ -13,10 +13,11 @@ class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget { final List? actions; final Function? onBackPress; final bool showHomeActionButton; + final bool showBackButton; final VoidCallback? onWillPopScope; final TextStyle? titleStyle; - const DefaultAppBar({this.title, this.onBackPress, this.onWillPopScope, this.actions,this.titleStyle, this.showHomeActionButton = false, Key? key}) : super(key: key); + const DefaultAppBar({this.title, this.onBackPress, this.onWillPopScope, this.actions,this.titleStyle, this.showHomeActionButton = false, this.showBackButton = true, Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -42,6 +43,7 @@ class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget { titleSpacing: 16, title: Row( children: [ + if(showBackButton) const Icon(Icons.arrow_back_ios).onPress(() { if (onWillPopScope != null) { showDialog( diff --git a/lib/new_views/common_widgets/single_item_drop_down_menu.dart b/lib/new_views/common_widgets/single_item_drop_down_menu.dart index d78e5f69..ce7f7838 100644 --- a/lib/new_views/common_widgets/single_item_drop_down_menu.dart +++ b/lib/new_views/common_widgets/single_item_drop_down_menu.dart @@ -2,6 +2,7 @@ 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/enum_extensions.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/new_views/app_style/app_text_style.dart'; @@ -27,10 +28,12 @@ class SingleItemDropDownMenu exte final bool showShadow; final bool showCancel; final Color? backgroundColor; // Now nullable + final Color? titleTextColor; // Now nullable final bool? loading; // Now nullable final int? requestById; final String? Function(T?)? validator; final AutovalidateMode? autovalidateMode; + final DropDownMatchType matchBy; /// To use a static data (without calling API) /// just send [NullableLoadingProvider] as generic data type and fill the [staticData] @@ -49,10 +52,12 @@ class SingleItemDropDownMenu exte this.showShadow = true, this.showCancel = false, this.backgroundColor, + this.titleTextColor, this.loading, this.requestById, this.validator, this.autovalidateMode, + this.matchBy = DropDownMatchType.identifier, }) : super(key: key); @override @@ -65,41 +70,97 @@ class _SingleItemDropDownMenuState(widget.context); } - if (widget.initialValue != null) { - final result = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.where((element) => element.identifier == widget.initialValue?.identifier); - if (result?.isNotEmpty ?? false) _selectedItem = result!.first as T?; - if (widget.onSelect != null && (widget.initialValue?.identifier ?? "") != (_selectedItem?.identifier ?? "")) { - widget.onSelect!(_selectedItem); // Non-null assertion after null check - } + if (widget.initialValue == null) return; + final List? items = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.cast(); + + if (items == null || items.isEmpty) return; + final Iterable matches = items.where((e) => _isSameItem(e, widget.initialValue!)); + + if (matches.isNotEmpty) { + _selectedItem = matches.first; + } else { + _selectedItem = null; + } + if (widget.onSelect != null && !_isSameItem(widget.initialValue!, _selectedItem ?? widget.initialValue!)) { + widget.onSelect!(_selectedItem); } - super.initState(); } + // @override + // void initState() { + // if (X != NullableLoadingProvider) { + // provider = Provider.of(widget.context); + // } + // if (widget.initialValue != null) { + // final result = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.where((element) => element.identifier == widget.initialValue?.identifier); + // if (result?.isNotEmpty ?? false) _selectedItem = result!.first as T?; + // if (widget.onSelect != null && (widget.initialValue?.identifier ?? "") != (_selectedItem?.identifier ?? "")) { + // widget.onSelect!(_selectedItem); // Non-null assertion after null check + // } + // } + // super.initState(); + // } + @override void setState(VoidCallback fn) { if (mounted) super.setState(fn); } + bool _isSameItem(Base a, Base b) { + switch (widget.matchBy) { + case DropDownMatchType.name: + return (a.name ?? '') == (b.name ?? ''); + case DropDownMatchType.identifier: + default: + return (a.identifier ?? '') == (b.identifier ?? ''); + } + } + + // @override + // void didUpdateWidget(covariant SingleItemDropDownMenu oldWidget) { + // if (widget.initialValue != null) { + // final result = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.where((element) => element.identifier == widget.initialValue?.identifier); + // + // if (result?.isNotEmpty ?? false) { + // _selectedItem = result?.first as T?; + // } else { + // _selectedItem = null; + // } + // if (widget.onSelect != null && (widget.initialValue?.identifier ?? "") != (_selectedItem?.identifier ?? "")) { + // widget.onSelect!(_selectedItem); // Non-null assertion after null check + // } + // } else { + // _selectedItem = null; + // } + // super.didUpdateWidget(oldWidget); + // } @override void didUpdateWidget(covariant SingleItemDropDownMenu oldWidget) { - if (widget.initialValue != null) { - final result = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.where((element) => element.identifier == widget.initialValue?.identifier); + super.didUpdateWidget(oldWidget); - if (result?.isNotEmpty ?? false) { - _selectedItem = result?.first as T?; - } else { - _selectedItem = null; - } - if (widget.onSelect != null && (widget.initialValue?.identifier ?? "") != (_selectedItem?.identifier ?? "")) { - widget.onSelect!(_selectedItem); // Non-null assertion after null check - } - } else { + if (oldWidget.initialValue == widget.initialValue) return; + + if (widget.initialValue == null) { _selectedItem = null; + return; + } + final List? items = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.cast(); + + T? matchedItem; + + if (items != null && items.isNotEmpty) { + final Iterable matches = items.where((e) => _isSameItem(e, widget.initialValue!)); + + if (matches.isNotEmpty) matchedItem = matches.first; + } + _selectedItem = matchedItem; + if (widget.onSelect != null && !_isSameItem(widget.initialValue!, _selectedItem ?? widget.initialValue!)) { + widget.onSelect!(_selectedItem); } - super.didUpdateWidget(oldWidget); } @override @@ -146,7 +207,7 @@ class _SingleItemDropDownMenuState( value: _selectedItem, 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 063df7dd..26cb28a3 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 @@ -114,25 +114,31 @@ 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)); } + //TODO uncommit this to enable task. list.add(CreateRequestModel(context.translation.task, "add_icon", CreateTaskView.id)); - list.add(CreateRequestModel("Medical Gas Inspection", "add_icon", CreateMedicalGasRequestPage.id)); + //Need to add check from permission Api. + // if (Provider.of(context, listen: false).engineerCanCreateCM) { + list.add(CreateRequestModel('Medical Gas Inspection'.addTranslation, "add_icon", CreateMedicalGasRequestPage.id)); + // } } else { list.add(CreateRequestModel(context.translation.correctiveMaintenance, "add_icon", CreateCMRequest.id)); list.add(CreateRequestModel(context.translation.gasRefill, "add_icon", GasRefillRequestForm.routeName)); 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)); list.add(CreateRequestModel('Demo Request'.addTranslation, "add_icon", CreateDemoRequestPage.id)); } 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 ea9963d2..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,8 +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; + 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) { @@ -93,18 +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) { if (context.settingProvider.isUserFlowMedical) types = {'Recall and Alert': 7}; - types['Equipment Internal Audit'] = 10; - types['System Internal Audit'] = 11; + if (context.settingProvider.isUserFlowMedical) types['Equipment Internal Audit'] = 10; + if (context.settingProvider.isUserFlowMedical) types['System Internal Audit'] = 11; + } + 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 0d96c648..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 @@ -37,6 +37,7 @@ class _MyRequestsPageState extends State { @override Widget build(BuildContext context) { +//TODO need to refactor this and move out of build... if (_provider == null) { requestsList = [ Request(null, context.translation.allWorkOrder), @@ -61,12 +62,16 @@ 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(14, "Medical Gas Inspection")); } requestsList.add(Request(12, "Loan Equipment")); @@ -74,15 +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), if (context.settingProvider.isUserFlowMedical) Request(7, 'Recall and Alert'), - Request(10, 'Equipment Internal Audit'), - Request(11, 'System Internal Audit'), + if (context.settingProvider.isUserFlowMedical) Request(10, 'Equipment Internal Audit'), + if (context.settingProvider.isUserFlowMedical) Request(11, 'System Internal Audit'), ]; } @@ -92,7 +104,6 @@ class _MyRequestsPageState extends State { _provider!.getAllRequests(context); }); } - return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, diff --git a/lib/new_views/pages/land_page/requests/request_paginated_listview.dart b/lib/new_views/pages/land_page/requests/request_paginated_listview.dart index 0b76bf87..f4d83656 100644 --- a/lib/new_views/pages/land_page/requests/request_paginated_listview.dart +++ b/lib/new_views/pages/land_page/requests/request_paginated_listview.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:test_sa/extensions/int_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/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_item_view.dart'; import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart'; import 'package:test_sa/modules/asset_inventory_module/pages/inventory_session_item_view.dart'; import 'package:test_sa/modules/loan_module/pages/loan_equipment_item_view.dart'; +import 'package:test_sa/modules/medical_gas_inspection/medical_gas_inspection_item_view.dart'; import 'package:test_sa/modules/tm_module/tasks/task_request_item_view.dart'; import 'package:test_sa/modules/traf_module/traf_request_item_view.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; @@ -75,6 +77,10 @@ class RequestPaginatedListview extends StatelessWidget { return SystemInternalAuditItemView(requestData: request); case 12: return LoanEquipmentItemView(requestData: request); + case 13: + return AssetDeliveryItemView(requestData: request); + case 14: + return MedicalGasInspectionItemView(requestData: request); default: return Container( height: 100, 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/new_views/pages/land_page/widgets/request_item_view_list.dart b/lib/new_views/pages/land_page/widgets/request_item_view_list.dart index 23512cee..10339da7 100644 --- a/lib/new_views/pages/land_page/widgets/request_item_view_list.dart +++ b/lib/new_views/pages/land_page/widgets/request_item_view_list.dart @@ -6,10 +6,12 @@ import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/all_requests_and_count_model.dart'; +import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_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/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart'; import 'package:test_sa/modules/asset_inventory_module/pages/inventory_session_item_view.dart'; import 'package:test_sa/modules/loan_module/pages/loan_equipment_item_view.dart'; +import 'package:test_sa/modules/medical_gas_inspection/medical_gas_inspection_item_view.dart'; import 'package:test_sa/modules/tm_module/tasks/task_request_item_view.dart'; import 'package:test_sa/modules/traf_module/traf_request_item_view.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; @@ -71,6 +73,11 @@ class RequestItemViewList extends StatelessWidget { return SystemInternalAuditItemView(requestDetails: list[index]); case 12: return LoanEquipmentItemView(requestDetails: list[index]); + case 13: + return AssetDeliveryItemView(requestDetails: list[index]); + case 14: + return MedicalGasInspectionItemView(requestDetails: list[index]); + default: Container( height: 100, 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/requests/request_status.dart b/lib/views/widgets/requests/request_status.dart index 272ddf42..ab7a40e5 100644 --- a/lib/views/widgets/requests/request_status.dart +++ b/lib/views/widgets/requests/request_status.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/text_extensions.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index a7edc369..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.4.0+31 +version: 1.5.0+32 environment: sdk: ">=3.5.0 <4.0.0"