diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index efd747ca..d427ca8d 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 = "42"; + static const String appReleaseBuildNumber = "44"; // static const host1 = "https://atomsm.hmg.com"; // production url // static const host1 = "https://atomsmdev.hmg.com"; // local DEV url @@ -74,10 +74,15 @@ class URLs { //One Time Task Urls cleaner module new flow urls .... static get getTaskType => "$_baseUrl/OneTimeTasks/GetTaskTypes"; + static get scanQRCode => "$_baseUrl/OneTimeTasks/ScanQRCode"; + static get proceedRequest => "$_baseUrl/OneTimeTasks/Proceed"; + static get completeRequest => "$_baseUrl/OneTimeTasks/Complete"; + static get getRequestById => "$_baseUrl/OneTimeTasks/GetById"; + //service request new flow urls. static get nurseDashboardCountUrl => '$_baseUrl/ServiceRequest/GetDashboardNurseCount'; @@ -161,6 +166,8 @@ class URLs { static get uploadWorkOrderAttachmentsUrl => '$_baseUrl/ServiceRequest/UploadAttachmentsWorkOrder'; + static get workOrderByAssetIdAutoComplete => '$_baseUrl/ServiceRequest/WorkOrderByAssetIdAutoComplete'; + static get getArrivalVerificationTypeUrl => '$_baseUrl/ArrivalVerificationType/GetArrivalVerificationType'; static get sendOtpUrl => '$_baseUrl/SmsNotification/SendOTP/'; diff --git a/lib/dashboard_latest/dashboard_view.dart b/lib/dashboard_latest/dashboard_view.dart index 88db0502..ca986cc1 100644 --- a/lib/dashboard_latest/dashboard_view.dart +++ b/lib/dashboard_latest/dashboard_view.dart @@ -13,6 +13,7 @@ import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; import 'package:test_sa/dashboard_latest/dashboard_provider.dart'; import 'package:test_sa/dashboard_latest/widgets/app_bar_widget.dart'; import 'package:test_sa/dashboard_latest/widgets/progress_fragment.dart'; +import 'package:test_sa/dashboard_latest/widgets/request_category_fragment_housekeeper.dart'; import 'package:test_sa/dashboard_latest/widgets/requests_fragment.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; @@ -58,17 +59,22 @@ class _DashboardViewState extends State { _dashBoardProvider.setTabs(userType: userProvider.user!.type!, context: context); _dashBoardProvider.getDashBoardCount(usersType: userProvider.user!.type!); _dashBoardProvider.resetRequestDataList(); - _dashBoardProvider.currentListIndex = null; - Provider.of(context, listen: false) - ..reset() - ..getAllRequests(context); - - // _dashBoardProvider.getRequestDetail( - // usersType: userProvider.user!.type!, - // status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].tag, - // tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].id, - // isHighPriority: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].isHighPriority, - // isOverdue: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].isOverDue); + + if (userProvider.isHouseKeeper) { + _dashBoardProvider.currentListIndex = 0; + _dashBoardProvider.getRequestDetail( + usersType: userProvider.user!.type!, + showLoader: true, + status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex!].tag, + tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex!].id, + // isAscending: _dashBoardProvider.isAscending, + ); + } else { + _dashBoardProvider.currentListIndex = null; + Provider.of(context, listen: false) + ..reset() + ..getAllRequests(context); + } } void getInitialData() { @@ -145,7 +151,7 @@ class _DashboardViewState extends State { children: [ 16.height, ProgressFragment(), 16.height, // const RequestsFragment(), - const RequestCategoryFragment() + context.userProvider.isHouseKeeper ? const RequestCategoryFragmentHouseKeeper() : const RequestCategoryFragment() ], ), ), diff --git a/lib/dashboard_latest/widgets/request_category_fragment.dart b/lib/dashboard_latest/widgets/request_category_fragment.dart index 488ff6a0..c452d306 100644 --- a/lib/dashboard_latest/widgets/request_category_fragment.dart +++ b/lib/dashboard_latest/widgets/request_category_fragment.dart @@ -80,6 +80,7 @@ class _RequestCategoryFragmentState extends State with // Reload data with new sort order dashboardProvider.resetRequestDataList(); + Provider.of(context, listen: false).reset(); if (dashboardProvider.listIndex.isEmpty) { // No category selected - do nothing or could reload default @@ -198,7 +199,7 @@ class _RequestCategoryFragmentState extends State with mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ 'All Tasks'.addTranslation.heading4(context), - 'add_icon'.toSvgAsset(width: 20, height: 20).onPress(() { + 'add_icon'.toSvgAsset(width: 24, height: 24,).onPress(() { Navigator.of(context).pushNamed('/cleaner_search'); }), ], diff --git a/lib/dashboard_latest/widgets/request_category_fragment_housekeeper.dart b/lib/dashboard_latest/widgets/request_category_fragment_housekeeper.dart new file mode 100644 index 00000000..38f1e95a --- /dev/null +++ b/lib/dashboard_latest/widgets/request_category_fragment_housekeeper.dart @@ -0,0 +1,240 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/dashboard_latest/dashboard_provider.dart'; +import 'package:test_sa/dashboard_latest/widgets/request_category_list.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/enums/user_types.dart'; +import 'package:test_sa/models/new_models/dashboard_detail.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/tab_button.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; + +/// Separate component for HouseKeeper role +/// HouseKeeper has simpler requirements: +/// - Single-select only (no multi-select) +/// - Uses getRequestDetail API only +/// - Default shows first category (In Progress) +class RequestCategoryFragmentHouseKeeper extends StatefulWidget { + const RequestCategoryFragmentHouseKeeper({Key? key}) : super(key: key); + + @override + State createState() => _RequestCategoryFragmentHouseKeeperState(); +} + +class _RequestCategoryFragmentHouseKeeperState extends State with SingleTickerProviderStateMixin { + bool isAscending = false; + late AnimationController _animationController; + late Animation _headerFadeAnimation; + late Animation _headerSlideAnimation; + final ScrollController _scrollController = ScrollController(); + // int _selectedIndex = 0; // Default first tab + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(milliseconds: 1200), + vsync: this, + ); + + // Header animation (appears first, faster) + _headerFadeAnimation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _animationController, + curve: const Interval(0.0, 0.4, curve: Curves.easeOut), + ), + ); + + _headerSlideAnimation = Tween( + begin: const Offset(0.0, -0.3), + end: Offset.zero, + ).animate( + CurvedAnimation( + parent: _animationController, + curve: const Interval(0.0, 0.5, curve: Curves.easeOutCubic), + ), + ); + + _animationController.forward(); + + // Load first category data on init + WidgetsBinding.instance.addPostFrameCallback((_) { + _loadCategoryData(0); + }); + } + + @override + void dispose() { + _animationController.dispose(); + _scrollController.dispose(); + super.dispose(); + } + + + void _loadCategoryData(int index) { + final dashboardProvider = Provider.of(context, listen: false); + List tabs = CategoryTabs.getTabs(userType: UsersTypes.houseKeeper, context: context); + + if (index >= 0 && index < tabs.length) { + dashboardProvider.resetRequestDataList(); + dashboardProvider.getRequestDetail( + usersType: UsersTypes.houseKeeper, + showLoader: true, + status: tabs[index].tag, + tabId: tabs[index].id, + isAscending: isAscending, + ); + } + } + + void _toggleSort(DashBoardProvider dashboardProvider) { + setState(() { + isAscending = !isAscending; + }); + + // Reload current category with new sort order + _loadCategoryData(dashboardProvider.currentListIndex!); + } + + @override + Widget build(BuildContext context) { + return Consumer(builder: (context, dashboardProvider, child) { + List tabs = CategoryTabs.getTabs(userType: UsersTypes.houseKeeper, context: context); + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Tabs + _buildTabs(tabs, dashboardProvider), + + // Header with sort + FadeTransition( + opacity: _headerFadeAnimation, + child: SlideTransition( + position: _headerSlideAnimation, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + 'All Tasks'.addTranslation.heading4(context), + Transform.flip( + flipY: isAscending, + child: 'dashboard/sort_descending'.toSvgAsset( + width: 24, + height: 24, + color: AppColor.headingTextColor(context), + ).onPress(()=>_toggleSort(dashboardProvider)), + ) + ], + ).paddingOnly(start: 16, end: 16), + ), + ), + + // Data list + dashboardProvider.isDetailLoading + ? Column( + mainAxisSize: MainAxisSize.max, + children: List.generate(3, (index) { + return Padding( + padding: EdgeInsets.symmetric(vertical: 10.toScreenHeight, horizontal: 0), + child: const SizedBox().toRequestShimmer(context, dashboardProvider.isDetailLoading), + ); + }), + ).paddingOnly(start: 16, end: 16, top: 16) + : (dashboardProvider.requestDetailList == null || dashboardProvider.requestDetailList?.data?.isEmpty == true) + ? const NoDataFound().paddingOnly(top: 80).center + : RequestCategoryList( + dashboardProvider.requestDetailList?.data ?? [], + dashboardProvider.isDetailLoading, + dashboardProvider.requestDetailList?.totalRows ?? 0, + ), + ], + ); + }); + } + + Widget _buildTabs(List tabs, DashBoardProvider dashboardProvider) { + // Calculate item width + final screenWidth = MediaQuery.of(context).size.width; + final isTablet = context.isTablet(); + final visibleItemsWithPeek = isTablet ? 4.5 : 3.5; + const spacing = 4.0; + const horizontalPadding = 16.0; + + final availableWidth = screenWidth - (horizontalPadding * 2); + final fullItems = visibleItemsWithPeek.floor(); + final totalSpacing = (fullItems - 1) * spacing + (spacing * 0.5); + final itemWidth = (availableWidth - totalSpacing) / visibleItemsWithPeek; + + return SizedBox( + height: itemWidth + 16, + child: ListView.separated( + controller: _scrollController, + scrollDirection: Axis.horizontal, + separatorBuilder: (cxt, index) => const SizedBox(width: 4), + padding: const EdgeInsets.only(bottom: 16, left: horizontalPadding, right: horizontalPadding), + itemBuilder: (cxt, index) { + // Animation + final delay = index * 0.08; + final animation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _animationController, + curve: Interval( + delay.clamp(0.0, 0.8), + (delay + 0.3).clamp(0.0, 1.0), + curve: Curves.easeOut, + ), + ), + ); + + final slideAnimation = Tween( + begin: const Offset(-0.3, 0.0), + end: Offset.zero, + ).animate( + CurvedAnimation( + parent: _animationController, + curve: Interval( + delay.clamp(0.0, 0.8), + (delay + 0.3).clamp(0.0, 1.0), + curve: Curves.easeOutCubic, + ), + ), + ); + + return FadeTransition( + opacity: animation, + child: SlideTransition( + position: slideAnimation, + child: SizedBox( + width: itemWidth, + child: RequestTypeButton( + label: tabs[index].label, + loading: dashboardProvider.isDetailLoading && dashboardProvider.currentListIndex == index, + isSelected: dashboardProvider.currentListIndex == index, + count: dashboardProvider.getStatusCount(tabs[index].requestType), + onPressed: () { + setState(() { + isAscending = false; + // _selectedIndex = index; + }); + dashboardProvider.currentListIndex = index; + _loadCategoryData(index); + }, + icon: tabs[index].icon, + iconColor: tabs[index].iconColor, + ), + ), + ), + ); + }, + itemCount: tabs.length, + ), + ); + } +} + diff --git a/lib/models/wo_by_asset_id_model.dart b/lib/models/wo_by_asset_id_model.dart new file mode 100644 index 00000000..8a6497d1 --- /dev/null +++ b/lib/models/wo_by_asset_id_model.dart @@ -0,0 +1,11 @@ +class WoByAssetIdModel { + int? id; + String? workOrderNo; + + WoByAssetIdModel({this.id, this.workOrderNo}); + + WoByAssetIdModel.fromJson(dynamic json) { + id = json['id']; + workOrderNo = json['workOrderNo']; + } +} diff --git a/lib/modules/cm_module/cm_detail_provider.dart b/lib/modules/cm_module/cm_detail_provider.dart index a63dcf9e..9aa4aa4d 100644 --- a/lib/modules/cm_module/cm_detail_provider.dart +++ b/lib/modules/cm_module/cm_detail_provider.dart @@ -17,6 +17,7 @@ import 'package:test_sa/models/new_models/dashboard_detail.dart'; import 'package:test_sa/models/new_models/work_order_detail_model.dart'; import 'package:test_sa/models/service_request/spare_parts.dart'; import 'package:test_sa/models/service_request/supplier_details.dart'; +import 'package:test_sa/models/wo_by_asset_id_model.dart'; import 'cm_request_utils.dart'; @@ -1003,4 +1004,19 @@ class CMDetailProvider extends ChangeNotifier { return -1; } } + + Future> getWorkOrderByAssetId(String searchText, int assetId) async { + late Response response; + try { + response = await ApiManager.instance.post(URLs.workOrderByAssetIdAutoComplete, body: {"pageSize": 1, "pageNumber": 20, "search": searchText, "assetId": assetId}); + List woList = []; + if (response.statusCode >= 200 && response.statusCode < 300) { + List categoriesListJson = json.decode(response.body)["data"]; + woList = categoriesListJson.map((wo) => WoByAssetIdModel.fromJson(wo)).toList(); + } + return woList; + } catch (error) { + return []; + } + } } diff --git a/lib/modules/cm_module/views/components/service_request_detail_view.dart b/lib/modules/cm_module/views/components/service_request_detail_view.dart index 938240fa..c6fef7db 100644 --- a/lib/modules/cm_module/views/components/service_request_detail_view.dart +++ b/lib/modules/cm_module/views/components/service_request_detail_view.dart @@ -198,6 +198,7 @@ class _ServiceRequestDetailViewState extends State { InfoTextWidget(label: context.translation.serialNo, value: workOrder.asset!.assetSerialNo), InfoTextWidget(label: context.translation.manufacture, value: workOrder.manufacturer?.name?.cleanupWhitespace.capitalizeFirstOfEach), InfoTextWidget(label: context.translation.model, value: workOrder.model?.name!.cleanupWhitespace.capitalizeFirstOfEach), + InfoTextWidget(label: context.translation.assetType, value: workOrder.assetType?.name!.cleanupWhitespace.capitalizeFirstOfEach), InfoTextWidget(label: context.translation.site, value: workOrder.site?.siteName?.cleanupWhitespace.capitalizeFirstOfEach), InfoTextWidget(label: context.translation.building, value: workOrder.building?.name?.cleanupWhitespace.capitalizeFirstOfEach), InfoTextWidget(label: context.translation.floor, value: workOrder.floor?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? ""), diff --git a/lib/modules/incident_module/create_incident_request_page.dart b/lib/modules/incident_module/create_incident_request_page.dart index e00c4149..3e8e0aad 100644 --- a/lib/modules/incident_module/create_incident_request_page.dart +++ b/lib/modules/incident_module/create_incident_request_page.dart @@ -16,12 +16,14 @@ 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/site.dart'; +import 'package:test_sa/models/wo_by_asset_id_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/incident_module/incident_lookup_provider.dart'; import 'package:test_sa/modules/incident_module/incident_provider.dart'; import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; import 'package:test_sa/modules/loan_module/provider/medical_department_provider.dart'; +import 'package:test_sa/modules/traf_module/asset_auto_complete_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'; @@ -31,6 +33,7 @@ import 'package:test_sa/providers/gas_request_providers/site_provider.dart'; import 'package:test_sa/views/widgets/date_and_time/time_picker.dart'; import 'package:test_sa/providers/loading_list_notifier.dart'; import 'package:test_sa/providers/lookups/yes_no_lookup_provider.dart'; +import 'package:test_sa/views/widgets/auto_complete/wo_auto_complete_field.dart'; import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; import 'package:test_sa/views/widgets/equipment/asset_picker.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; @@ -64,6 +67,7 @@ class _CreateIncidentRequestPageState extends State { Department? _selectedDepartment; Asset? device; + WoByAssetIdModel? woByAssetId; Lookup? incidentType; Lookup? ovrSystem; @@ -128,10 +132,10 @@ class _CreateIncidentRequestPageState extends State { showBorder: true, onPick: (asset) async { device = asset; - payload["assetId"] = asset.modelDefinition?.id; + payload["assetId"] = asset?.id; payload["oracleCode"] = asset.modelDefinition?.oracleCodes?.first.codeValue; payload["model"] = asset.modelDefinition?.modelName; - payload["manufacturer"] = asset.modelDefinition?.manufacturerId; + payload["manufacturer"] = asset.modelDefinition?.manufacturerName; payload["assetOrigin"] = asset.modelDefinition?.assetName; payload["siteId"] = asset.site?.id; payload["buildingId"] = asset.building?.id; @@ -139,7 +143,19 @@ class _CreateIncidentRequestPageState extends State { payload["departmentId"] = asset.department?.id; setState(() {}); }, - ) + ), + 12.height, + WoAutoCompleteField( + clearAfterPick: false, + byName: true, + enabled: device != null, + assetId: device?.id?.toInt(), + initialValue: "", + onPick: (asset) { + woByAssetId = asset; + setState(() {}); + }, + ), ], if (incidentType?.value != null && incidentType?.value != 1) ...[ 16.height, @@ -358,6 +374,7 @@ class _CreateIncidentRequestPageState extends State { onSelect: (value) { incidentType = value; payload["incidentClassificationId"] = value?.id; + payload["workOrderId"] = null; setState(() {}); }, ), @@ -661,17 +678,19 @@ class _CreateIncidentRequestPageState extends State { "Please scan or pick asset".showToast; return; } - + if (incidentType?.value == 1 && device != null && woByAssetId?.id == null) { + "Please choose CM Work Order".showToast; + return; + } if (ovrSystem?.value == 1 && isOvrVerified == false) { "Please verify OVR Ticket Number".showToast; return; } - if (occurrenceDate == null) { "Please select occurrence Data".showToast; return; } - + payload["workOrderId"] = woByAssetId?.id; payload["occurrenceDate"] = occurrenceDate!.toIso8601String(); _formKey.currentState!.save(); diff --git a/lib/modules/incident_module/incident_data_model.dart b/lib/modules/incident_module/incident_data_model.dart index 6e5e46f1..7c9f3ba7 100644 --- a/lib/modules/incident_module/incident_data_model.dart +++ b/lib/modules/incident_module/incident_data_model.dart @@ -74,6 +74,8 @@ class IncidentDataModel { String? occurrenceDate; String? createdDate; List? incidentAttachments; + int? workOrderId; + String? workOrderNo; IncidentDataModel( {this.id, @@ -148,6 +150,8 @@ class IncidentDataModel { this.approvalSignature, this.occurrenceDate, this.createdDate, + this.workOrderId, + this.workOrderNo, this.incidentAttachments}); IncidentDataModel.fromJson(Map json) { @@ -223,6 +227,8 @@ class IncidentDataModel { approvalSignature = json['approvalSignature']; occurrenceDate = json['occurrenceDate']; createdDate = json['createdDate']; + workOrderId = json['workOrderId']; + workOrderNo = json['workOrderNo']; if (json['incidentAttachments'] != null) { incidentAttachments = []; json['incidentAttachments'].forEach((v) { @@ -305,6 +311,8 @@ class IncidentDataModel { data['approvalSignature'] = this.approvalSignature; data['occurrenceDate'] = this.occurrenceDate; data['createdDate'] = this.createdDate; + data['workOrderId'] = this.workOrderId; + data['workOrderNo'] = this.workOrderNo; if (this.incidentAttachments != null) { data['incidentAttachments'] = this.incidentAttachments!.map((v) => v.toJson()).toList(); } diff --git a/lib/modules/incident_module/incident_detail_page.dart b/lib/modules/incident_module/incident_detail_page.dart index ea8f37c4..b131c0eb 100644 --- a/lib/modules/incident_module/incident_detail_page.dart +++ b/lib/modules/incident_module/incident_detail_page.dart @@ -1,3 +1,6 @@ +import 'dart:developer'; + +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; @@ -6,6 +9,8 @@ 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/cm_detail_page.dart'; +import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/modules/incident_module/incident_attachment_model.dart'; import 'package:test_sa/modules/incident_module/incident_data_model.dart'; import 'package:test_sa/modules/incident_module/incident_provider.dart'; @@ -88,6 +93,18 @@ class IncidentDetailPage extends StatelessWidget { InfoTextWidget(label: 'Floor', value: incidentData.floorName ?? "-"), InfoTextWidget(label: 'Department', value: incidentData.departmentName ?? "-"), InfoTextWidget(label: 'Occurrence Date', value: incidentData.occurrenceDate?.toServiceRequestDetailsFormat ?? "-"), + if (incidentData.workOrderId != null) + RichText( + text: TextSpan(text: "Work Order: ", style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20), children: [ + TextSpan( + text: "${incidentData.workOrderNo}", + style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context), decoration: TextDecoration.underline), + recognizer: TapGestureRecognizer() + ..onTap = () async { + await Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(requestId: incidentData.workOrderId!, moduleId: 3))); + }), + ]), + ), InfoTextWidget(label: 'Root Cause', value: incidentData.rootCauseName ?? "-"), if (incidentData.rootCauseValue == 5) InfoTextWidget(label: 'Root reason', value: incidentData.otherRootCause ?? "-"), InfoTextWidget(label: 'Comments', value: incidentData.comments ?? "-"), 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 c4c9042f..d5f8163c 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 @@ -236,6 +236,7 @@ class _CreateMedicalGasRequestPageState extends State CreateMedicalGasRequestPage( - dataModel: model, - ))); + bool? isRefresh = await Navigator.of(context).push(MaterialPageRoute(builder: (_) => CreateMedicalGasRequestPage(dataModel: model))); if (isRefresh == true) { _loadData(); } 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 index 323fcfba..b6fa4b55 100644 --- a/lib/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart +++ b/lib/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart @@ -49,7 +49,7 @@ class AttributeInfo { AttributeInfo.fromJson(Map json) { id = json['id']; medicalGasAttributeId = json['medicalGasAttributeId']; - requestedQuantity = json['requestedQuantity']; + requestedQuantity = json['value'] ?? json['requestedQuantity']; value = json['value']; itemName = json['itemName']; oracleName = json['oracleName']; diff --git a/lib/modules/medical_gas_inspection/update_delivery_notes_page.dart b/lib/modules/medical_gas_inspection/update_delivery_notes_page.dart index 1a0dfae5..c4fd21ba 100644 --- a/lib/modules/medical_gas_inspection/update_delivery_notes_page.dart +++ b/lib/modules/medical_gas_inspection/update_delivery_notes_page.dart @@ -1,3 +1,7 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/providers/api/all_requests_provider.dart'; @@ -6,6 +10,8 @@ import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/generic_attachment_model.dart'; +import 'package:test_sa/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/medical_gas_inspection/models/delivery_notes_form_model.dart'; import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_data_model.dart'; @@ -38,9 +44,7 @@ class _UpdateDeliveryNotesState extends State { bool pressureTestAcknowledgement = false; bool leakTestAcknowledgement = false; final GlobalKey _formKey = GlobalKey(); - final GlobalKey _scaffoldKey = GlobalKey(); - - // List selectedItemList = []; + List _attachments = []; @override void initState() { @@ -63,9 +67,13 @@ class _UpdateDeliveryNotesState extends State { isInspectedGasLeak: dataModel.isInspectedGasLeak, isPressureChecked: dataModel.isPressureChecked, deliveredDate: dataModel.deliveredDate, - attachments: dataModel.deliveryNoteAttachmentDto, + // attachments: dataModel.deliveryNoteAttachmentDto, isSubmitted: dataModel.isSubmitted, deliveryNoteNumber: dataModel.deliveryNote); + + if (dataModel.deliveryNoteAttachmentDto.isNotEmpty) { + _attachments.addAll(dataModel.deliveryNoteAttachmentDto.map((e) => GenericAttachmentModel(id: e.id, name: e.name)).toList()); + } //Need to Confirm this condition // selectedItemList = dataModel.requestDetailDtos.where((item) => item.deliveredQuantity != null && item.deliveredQuantity! > 0).toList(); setState(() {}); @@ -202,7 +210,7 @@ class _UpdateDeliveryNotesState extends State { AttachmentPicker( label: 'Upload Attachment', - attachment: formModel.attachments, + attachment: _attachments, buttonColor: AppColor.primary10, // showAsListView: true, onlyImages: false, @@ -319,6 +327,15 @@ class _UpdateDeliveryNotesState extends State { deliveredQuantity: item.deliveredQuantity, )); } + formModel.attachments = []; + try { + for (var item in _attachments) { + String fileName = CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? ''; + formModel.attachments.add(GenericAttachmentModel(id: item.id, name: fileName)); + } + } catch (error) { + print(error); + } showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); await medicalGasInspectionProvider.updatedDeliveryNotes(model: formModel).then((status) async { Navigator.pop(context); @@ -370,9 +387,9 @@ class _UpdateDeliveryNotesState extends State { return "Enter a valid number"; } - if (intVal <= 0) { - return "Value must be greater than 0"; - } + // if (intVal <= 0) { + // return "Value must be greater than 0"; + // } return null; }, diff --git a/lib/modules/pm_module/recurrent_wo/components/task_info_widget.dart b/lib/modules/pm_module/recurrent_wo/components/task_info_widget.dart index 7164d7b1..e76ca8bd 100644 --- a/lib/modules/pm_module/recurrent_wo/components/task_info_widget.dart +++ b/lib/modules/pm_module/recurrent_wo/components/task_info_widget.dart @@ -88,8 +88,8 @@ class _RecurrentTaskInfoWidgetState extends State { '${context.translation.floor}: ${widget.model!.floor?.name ?? "-"}'.bodyText2(context).custom(color: AppColor.neutral120), '${context.translation.room}: ${widget.model!.room?.name ?? "-"}'.bodyText2(context).custom(color: AppColor.neutral120), ], - InfoTextWidget(label: context.translation.assignedEmployee, value: widget.model!.engineer!.userName ?? ""), - InfoTextWidget(label: context.translation.scheduledDate, value: widget.model!.scheduleDate!.toMonthYearFormat), + InfoTextWidget(label: context.translation.assignedEmployee, value: widget.model!.engineer?.userName ?? ""), + InfoTextWidget(label: context.translation.scheduledDate, value: widget.model!.scheduleDate!.toServiceRequestDetailsFormat), ], ).toShadowContainer(context), 12.height, diff --git a/lib/modules/traf_module/traf_request_detail_page.dart b/lib/modules/traf_module/traf_request_detail_page.dart index d6d428d6..31fcf68e 100644 --- a/lib/modules/traf_module/traf_request_detail_page.dart +++ b/lib/modules/traf_module/traf_request_detail_page.dart @@ -125,8 +125,8 @@ class _TrafRequestDetailPageState extends State { 4.height, 'Requested Quantity: ${data.qty ?? '-'}'.bodyText(context), //todo @waseem replace with infotext ], - InfoTextWidget(label: 'How would the requested technology solve the current situation and/or serve the purpose?', value: data.purposeAnswer ?? '-'), - InfoTextWidget(label: 'What is the current practice?', value: data.currentPractise ?? '-'), + InfoTextWidget(label: 'How would the requested technology solve the current situation and/or serve the purpose?', value: "\n${data.purposeAnswer ?? '-'}"), + InfoTextWidget(label: 'What is the current practice?', value: "\n${data.currentPractise ?? '-'}"), InfoTextWidget(label: 'Census Q1', value: data.censusQ1 ?? '-'), InfoTextWidget(label: 'Census Q2', value: data.censusQ2 ?? '-'), InfoTextWidget(label: 'Census Q3', value: data.censusQ3 ?? '-'), @@ -136,15 +136,15 @@ class _TrafRequestDetailPageState extends State { value: "\n${data.trafContacts?.map((item) => item.name).toList().toString() ?? '-'}"), InfoTextWidget(label: 'Is the requesting department going to use the technology solely or shared with other departments?', value: "\n${data.usingSolelyOrSharedName ?? '-'}"), if ((data.departments ?? []).isNotEmpty) ...[ - InfoTextWidget(label: 'Technology used with other departments', value: data.departments?.map((item) => item.departmentName).toList().toString() ?? '-'), + InfoTextWidget(label: 'Technology used with other departments', value: "\n${data.departments?.map((item) => item.departmentName).toList().toString() ?? '-'}"), ], - InfoTextWidget(label: 'Would other services be effected by acquiring the new equipment?', value: data.isEffectedName ?? '-'), + InfoTextWidget(label: 'Would other services be effected by acquiring the new equipment?', value: "\n${data.isEffectedName ?? '-'}"), if ((data.effectedServices ?? "").isNotEmpty) ...[ - InfoTextWidget(label: 'List of services would it be effected', value: data.effectedServices ?? '-'), + InfoTextWidget(label: 'List of services would it be effected', value: "\n${data.effectedServices ?? '-'}"), ], - InfoTextWidget(label: 'Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?', value: data.isCombinationName ?? '-'), + InfoTextWidget(label: 'Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?', value: "\n${data.isCombinationName ?? '-'}"), if ((data.usedWithCombination ?? "").isNotEmpty) ...[ - InfoTextWidget(label: 'Equipment going to be used with combination of other equipments', value: data.usedWithCombination ?? '-'), + InfoTextWidget(label: 'Equipment going to be used with combination of other equipments', value: "\n${data.usedWithCombination ?? '-'}"), ], if (_attachments.isNotEmpty) ...[ 4.height, diff --git a/lib/modules/traf_module/traf_request_item_view.dart b/lib/modules/traf_module/traf_request_item_view.dart index 3e74c425..15223204 100644 --- a/lib/modules/traf_module/traf_request_item_view.dart +++ b/lib/modules/traf_module/traf_request_item_view.dart @@ -43,7 +43,6 @@ class TrafRequestItemView extends StatelessWidget { InfoDateWidget(requestData!.transactionDate ?? ""), ], ), - 8.height, InfoHeaderWidget(requestData?.typeTransaction ?? "Assessment of Need & Technology"), InfoTextWidget(label: context.translation.requestType, value: requestData?.requestTypeName), InfoTextWidget(label: "Request No", value: requestData?.requestNo, showArrow: true), @@ -74,7 +73,6 @@ class TrafRequestItemView extends StatelessWidget { InfoDateWidget(requestDetails!.date ?? ""), ], ), - 8.height, InfoHeaderWidget(requestDetails?.nameOfType ?? "Assessment of Need & Technology"), InfoTextWidget(label: context.translation.requestType, value: requestDetails!.requestType), InfoTextWidget(label: "Request No", value: requestDetails!.requestNo), diff --git a/lib/new_views/pages/land_page/land_page.dart b/lib/new_views/pages/land_page/land_page.dart index a52085a0..bd475186 100644 --- a/lib/new_views/pages/land_page/land_page.dart +++ b/lib/new_views/pages/land_page/land_page.dart @@ -98,7 +98,7 @@ class _LandPageState extends State { Navigator.pop(context); Navigator.of(context).pushNamed(SettingsPage.id); }, - label: "Enable".addTranslation), + label: "Continue".addTranslation), ], )), ), @@ -115,7 +115,9 @@ class _LandPageState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { _userProvider!.getSwipeLastTransaction(userId: _userProvider!.user!.userID!); _userProvider!.getSiteContactInfo(); - Provider.of(context, listen: false).getData(); + if(!_userProvider!.isHouseKeeper) { + Provider.of(context, listen: false).getData(); + } }); } _pages = [ diff --git a/lib/new_views/pages/land_page/requests/recurrent_wo_item_view.dart b/lib/new_views/pages/land_page/requests/recurrent_wo_item_view.dart index 1e8cd0ee..5b92db52 100644 --- a/lib/new_views/pages/land_page/requests/recurrent_wo_item_view.dart +++ b/lib/new_views/pages/land_page/requests/recurrent_wo_item_view.dart @@ -42,7 +42,6 @@ class RecurrentWoItemView extends StatelessWidget { InfoDateWidget(requestData!.transactionDate ?? ""), ], ), - 8.height, InfoHeaderWidget(requestData?.typeTransaction ?? context.translation.recurrentWo), InfoTextWidget(label: context.translation.taskNo, value: requestData!.requestNo), InfoTextWidget(label: context.translation.taskNo, value: requestData!.requestNo), @@ -79,7 +78,6 @@ class RecurrentWoItemView extends StatelessWidget { InfoDateWidget(requestDetails!.date ?? ""), ], ), - 8.height, InfoHeaderWidget(requestDetails?.nameOfType ?? context.translation.recurrentWo), InfoTextWidget( label: context.translation.taskNo, diff --git a/lib/views/widgets/auto_complete/wo_auto_complete_field.dart b/lib/views/widgets/auto_complete/wo_auto_complete_field.dart new file mode 100644 index 00000000..b157df2e --- /dev/null +++ b/lib/views/widgets/auto_complete/wo_auto_complete_field.dart @@ -0,0 +1,136 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/oracle_code_provider.dart'; +import 'package:test_sa/controllers/providers/api/parts_provider.dart'; +import 'package:test_sa/controllers/providers/api/user_provider.dart'; +import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; +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/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart'; +import 'package:test_sa/models/wo_by_asset_id_model.dart'; +import 'package:test_sa/modules/cm_module/cm_detail_provider.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/app_style/sizing.dart'; + +import '../../../extensions/text_extensions.dart'; +import '../../../models/service_request/spare_parts.dart'; +import '../../../new_views/app_style/app_text_style.dart'; + +class WoAutoCompleteField extends StatefulWidget { + final String initialValue; + final int? assetId; + final bool clearAfterPick, byName, enabled; + final Function(WoByAssetIdModel) onPick; + + const WoAutoCompleteField({Key? key, required this.byName, required this.initialValue, this.assetId, required this.onPick, this.clearAfterPick = true, this.enabled = true}) : super(key: key); + + @override + _WoAutoCompleteFieldState createState() => _WoAutoCompleteFieldState(); +} + +class _WoAutoCompleteFieldState extends State { + late CMDetailProvider _cmDetailProvider; + + late TextEditingController _controller; + + bool loading = false; + + @override + void initState() { + _controller = TextEditingController(text: widget.initialValue); + super.initState(); + _cmDetailProvider = Provider.of(context, listen: false); + } + + @override + void didUpdateWidget(covariant WoAutoCompleteField 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) { + 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( + optionsBuilder: (TextEditingValue textEditingValue) async { + if (textEditingValue.text.isEmpty) { + if (loading) { + setState(() { + loading = false; + }); + } + return const Iterable.empty(); + } + if (!loading) { + setState(() { + loading = true; + }); + } + List workOrders = (await _cmDetailProvider.getWorkOrderByAssetId(textEditingValue.text, widget.assetId!)); + + setState(() { + loading = false; + }); + return workOrders; + }, + displayStringForOption: (WoByAssetIdModel option) => widget.byName ? option.workOrderNo ?? "" : option.workOrderNo ?? "", + fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) { + return TextField( + controller: _controller, + 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(maxHeight: 24, maxWidth: 24 + 8), + filled: true, + enabled: widget.enabled, + 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: "CM Work Order Number", + labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.textColor(context)), + suffixIcon: loading ? const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 3.0).paddingOnly(end: 8) : null, + ), + textInputAction: TextInputAction.search, + onChanged: (text) { + fieldTextEditingController.text = text; + }, + onSubmitted: (String value) { + onFieldSubmitted(); + }, + ); + }, + onSelected: (WoByAssetIdModel selection) { + if (widget.clearAfterPick) { + _controller.clear(); + } else { + _controller.text = widget.byName ? (selection.workOrderNo ?? "") : (selection.workOrderNo ?? ""); + } + widget.onPick(selection); + }, + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 606ec3d8..23e7cefa 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.7.7+42 +version: 1.7.8+44 environment: sdk: ">=3.5.0 <4.0.0"