From 392136dbe4523d41d6213578301d77624d2e9703 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 <50428976+WaseemAbbasi22@users.noreply.github.com> Date: Wed, 19 Aug 2026 15:17:01 +0300 Subject: [PATCH] Document library CR completed ready for test --- lib/controllers/api_routes/urls.dart | 10 +- .../providers/api/devices_provider.dart | 36 ++++ lib/models/generic_attachment_model.dart | 48 +++++- .../pages/attachment_view.dart | 7 +- .../components/activities_list_view.dart | 15 +- .../service_request_detail_view.dart | 10 +- .../demo_module/demo_activities_page.dart | 12 +- .../incident_module/incident_detail_page.dart | 9 +- .../equipment_internal_audit_detail_page.dart | 14 +- .../system_internal_audit_detail_page.dart | 9 +- .../pages/loan_equipment_detail_page.dart | 9 +- .../medical_gas_inspection_detail_page.dart | 10 +- .../device_transfer_details.dart | 10 +- .../gas_refill/gas_refill_details.dart | 5 +- .../tasks/task_request_detail_view.dart | 5 +- .../traf_module/traf_request_detail_page.dart | 9 +- .../widgets/equipment/asset_detail_page.dart | 2 +- .../widgets/equipment/asset_details_view.dart | 9 +- .../asset_document_history_view.dart | 162 ++++++++++++++++-- lib/views/widgets/images/files_list.dart | 9 +- 20 files changed, 345 insertions(+), 55 deletions(-) diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 0dd6626e..620d367f 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -4,11 +4,11 @@ class URLs { static const String appReleaseBuildNumber = "50"; // static const host1 = "https://atomsm.hmg.com"; // production url - // static const host1 = "https://atomsmdev.hmg.com"; // local DEV url - static const host1 = "https://atomsmuat.hmg.com"; // local UAT url + static const host1 = "https://atomsmdev.hmg.com"; // local DEV url + // static const host1 = "https://atomsmuat.hmg.com"; // local UAT url - // static final String _baseUrl = "$_host/mobile"; // host local UAT - static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis + static final String _baseUrl = "$_host/mobile"; // host local UAT + // static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis // static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM // static final String _baseUrl = "$_host/v4/mobile"; // v4 for Demo module // static final String _baseUrl = "$_host/v5/mobile"; // v5 for data segregation @@ -374,6 +374,8 @@ class URLs { static get updateDeviceTransfer => "$_baseUrl/AssetTransfer/UpdateEngineerAssetTransferNew"; // get static get assetTransferAssignEngineerToHimself => "$_baseUrl/AssetTransfer/AssignEngineerToHimself"; // get static get assetTransferEngineerReject => "$_baseUrl/AssetTransfer/EngineerReject"; // get + static get assetDocumentHistory => "$_baseUrl/DocumentLibrary/SearchDocuments"; + // employee static get getEmployees => "$_baseUrl/Lookups/GetLookup?lookupEnum=33"; // get diff --git a/lib/controllers/providers/api/devices_provider.dart b/lib/controllers/providers/api/devices_provider.dart index 66c95d7b..c8b5b9c7 100644 --- a/lib/controllers/providers/api/devices_provider.dart +++ b/lib/controllers/providers/api/devices_provider.dart @@ -31,6 +31,7 @@ class AssetProvider extends ChangeNotifier { final pageItemNumber = 10; final searchPageItemNumber = 10; + final documentPageItemNumber = 20; bool nextPage = true; @@ -257,4 +258,39 @@ class AssetProvider extends ChangeNotifier { return false; } } + + Future<({List documents, int totalRows})> getAssetDocumentHistory({ + required int assetId, + int pageNumber = 1, + }) async { + Response response; + List documents = []; + try { + var body = { + "pageSize": documentPageItemNumber, + "pageNumber": pageNumber, + "assetIds": [assetId] + }; + response = await ApiManager.instance.post(URLs.assetDocumentHistory, body: body); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + final jsonData = json.decode(response.body); + List attachmentJson = jsonData["data"] ?? []; + int totalRows = jsonData["totalRows"] ?? 0; + + documents = attachmentJson + .map((request) => + GenericAttachmentModel.fromAssetDocumentJson(request)) + .toList(); + + nextPage = documents.length < totalRows; + + return (documents: documents, totalRows: totalRows); + } + return (documents: documents, totalRows: 0); + } catch (e) { + log('Error in getAssetDocumentHistory: $e'); + return (documents: documents, totalRows: 0); + } + } } diff --git a/lib/models/generic_attachment_model.dart b/lib/models/generic_attachment_model.dart index 6a6d552b..a59e5b0f 100644 --- a/lib/models/generic_attachment_model.dart +++ b/lib/models/generic_attachment_model.dart @@ -1,10 +1,14 @@ import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/models/lookup.dart'; + class GenericAttachmentModel { GenericAttachmentModel({ this.id, this.name, + this.module, + this.subModule, + this.uploadedBy, this.originalName, this.createdBy, this.documentType, @@ -20,6 +24,9 @@ class GenericAttachmentModel { int? id; String? name; + String ? module; + String ? subModule; + String ? uploadedBy; String? createdBy; String? originalName; Lookup? documentType; @@ -36,17 +43,32 @@ class GenericAttachmentModel { id = json['id']; name = json['name'] ?? json['attachmentName'] ?? json['imageName']; createdBy = json['createdBy']; - documentType = json['documentType']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; originalName = json['originalName'] ?? json['imageOriginalName']; - if (convertToLink) { name = URLs.getFileUrl(name); } } + GenericAttachmentModel.fromAssetDocumentJson(Map json, {bool convertToLink = false}) { + id = json['id']; + name = json['attachmentName'] ?? json['name'] ?? json['imageName']; + module = json['module']; + subModule = json['subModule']; + createdBy = json['createdBy']; + createdDate = json['createdDate']; + uploadedBy = json['uploadedBy']; + originalName = json['originalName'] ?? json['imageOriginalName']; + documentType = json['attachmentType'] != null ? Lookup(name: json['attachmentType'], id: 0) : null; + if (convertToLink && name != null) { + name = URLs.getFileUrl(name); + } + } + GenericAttachmentModel.fromWorkOrderJson(Map json) { id = json['id']; name = json['name']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; createdBy = json['createdBy']; } @@ -54,7 +76,7 @@ class GenericAttachmentModel { id = json['id']?.toInt(); name = json['attachmentName']; attachmentDescription = json['attachmentDescription']; - documentType = json['documentType']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; moduleReferenceId = json['loanId']; attachmentTypeId = json['loanAttachmentTypeId']; } @@ -64,6 +86,7 @@ class GenericAttachmentModel { name = json['attachmentName']; attachmentDescription = json['attachmentDescription']; moduleReferenceId = json['loanId']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; attachmentTypeId = json['loanAttachmentTypeId']; createdBy = json['createdBy']; createdDate = json['createdDate']; @@ -75,13 +98,16 @@ class GenericAttachmentModel { id = json['id']; name = json['attachmentName'] ?? json['name']; moduleReferenceId = json['visitId']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; attachmentURL = json['attachmentURL']; } + GenericAttachmentModel.fromSupplierJson(Map json) { id = json['id']; name = json['attachmentName'] ?? json['name']; supplierId = json['supplierId']; moduleReferenceId = json['visitId']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; attachmentURL = json['attachmentURL']; } @@ -89,7 +115,7 @@ class GenericAttachmentModel { id = json['id']; name = json['attachmentName']; moduleReferenceId = json['gasRefillId']; - documentType = json['documentType']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; } GenericAttachmentModel.fromIncidentJson(Map json) { @@ -97,19 +123,21 @@ class GenericAttachmentModel { name = json['attachmentName']; moduleReferenceId = json['incidentId']; attachmentDescription = json['attachmentDescription']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; } GenericAttachmentModel.fromAssetTransferJson(Map json) { id = json['id']; name = json['attachmentName']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; } GenericAttachmentModel.fromDemoJson(Map json) { id = json['id']; name = json['attachmentName']; originalName = json['originalName']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; moduleReferenceId = json['demoRequestId']; - documentType = json['documentType'] != null ? Lookup.fromJson(json['documentType']) : null; } GenericAttachmentModel.fromInternalAuditJson(Map json) { @@ -117,19 +145,20 @@ class GenericAttachmentModel { name = (json['name'] != null && !json['name'].toString().startsWith('data:image/jpeg')) ? json['name'] : null; originalName = json['originalName']; createdBy = json['createdBy']; - documentType = json['documentType']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; } GenericAttachmentModel.fromPreventiveVisitJson(Map json) { id = json['id']; name = json['attachmentName']; - documentType = json['documentType']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; } GenericAttachmentModel.fromAssetJson(Map json) { id = json['id']; name = json['attachmentName']; attachmentURL = json['attachmentURL']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; originalName = json['originalName']; } @@ -137,10 +166,9 @@ class GenericAttachmentModel { id = json['id']; name = json['attachmentName']; moduleReferenceId = json['trafId']; - documentType = json['documentType']; + documentType = json['attachmentType'] != null ? Lookup.fromJson(json['attachmentType']) : null; } - Map toJson() { final Map data = {}; data['id'] = id; @@ -156,6 +184,7 @@ class GenericAttachmentModel { data['id'] = id; data['name'] = name; data['createdBy'] = createdBy; + ///TODO need to keep same variable for document type in backend for all modules, currently it is different for different modules if (documentType != null) { data['attachmentTypeId'] = documentType?.id; @@ -189,6 +218,7 @@ class GenericAttachmentModel { data['modifiedDate'] = modifiedDate; return data; } + //TODO need to check where to use this need to verify with backend team. Map toPpmJson() { final Map data = {}; diff --git a/lib/modules/asset_delivery_module/pages/attachment_view.dart b/lib/modules/asset_delivery_module/pages/attachment_view.dart index 140c524b..ebcba992 100644 --- a/lib/modules/asset_delivery_module/pages/attachment_view.dart +++ b/lib/modules/asset_delivery_module/pages/attachment_view.dart @@ -97,7 +97,12 @@ class _AssetDeliveryAttachmentViewState extends State URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []).toShadowContainer( + : FilesList( showAsListView: true, + types: _attachments.map((doc) { + final attachmentType = doc.documentType?.name ?? ''; + return attachmentType.isNotEmpty ? attachmentType : 'Asset Guide'; + }).toList(), + images: _attachments.map((e) => URLs.getFileUrl(e.name ?? '') ?? '',).toList() ?? []).toShadowContainer( context, borderRadius: 20, padding: 12, diff --git a/lib/modules/cm_module/views/components/activities_list_view.dart b/lib/modules/cm_module/views/components/activities_list_view.dart index 4c6adb41..a4126d32 100644 --- a/lib/modules/cm_module/views/components/activities_list_view.dart +++ b/lib/modules/cm_module/views/components/activities_list_view.dart @@ -173,7 +173,18 @@ class _ActivitiesListViewState extends State { if (activity.activitySparePart?.acitiySparePartAttachments?.isNotEmpty ?? false) ...[ 2.height, const Divider().defaultStyle(context), - FilesList(images: activity.activitySparePart!.acitiySparePartAttachments!.whereType().where((e) => e.name != null).map((toElement) => URLs.getFileUrl(toElement.name!)!).toList()), + FilesList( + showAsListView: true, + types: activity.activitySparePart!.acitiySparePartAttachments! + .whereType() + .where((e) => e.documentType != null && e.documentType!.name!.isNotEmpty) + .map((toElement) => toElement.documentType!.name!) + .toList(), + images: activity.activitySparePart!.acitiySparePartAttachments! + .whereType() + .where((e) => e.name != null) + .map((toElement) => URLs.getFileUrl(toElement.name!)!) + .toList()), ], ], ).toShadowContainer(context, showShadow: false).onPress(() { @@ -217,7 +228,6 @@ class _ActivitiesListViewState extends State { ), ], ), - if (showActionButtons) Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -252,7 +262,6 @@ class _ActivitiesListViewState extends State { label: context.translation.assistantEmployee, value: activity.activityMaintenance?.assistantEmployees?.first.user?.userName, ), - ] else ...[ InfoTextWidget( label: context.translation.assignedEmployee, 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 ab0538e9..008bbe4a 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 @@ -270,7 +270,15 @@ class _ServiceRequestDetailViewState extends State { if (_attachments.isNotEmpty) ...[ const Divider().defaultStyle(context), InfoHeader16Widget("Attachments".addTranslation), - FilesList(images: _attachments.map((toElement) => URLs.getFileUrl(toElement.name!) ?? '').toList()), + FilesList( + showAsListView: true, + images: _attachments.map((toElement) => URLs.getFileUrl(toElement.name!) ?? '').toList(), + types: _attachments + .whereType() + .where((e) => e.documentType != null && e.documentType!.name!.isNotEmpty) + .map((toElement) => toElement.documentType!.name!) + .toList(), + ), ], if (!requestProvider.isReadOnlyRequest && workOrder.nextStep?.workOrderNextStepEnum == WorkOrderNextStepEnum.activity) ...[ const Divider().defaultStyle(context), diff --git a/lib/modules/demo_module/demo_activities_page.dart b/lib/modules/demo_module/demo_activities_page.dart index f9a7dc2c..a7baf9ef 100644 --- a/lib/modules/demo_module/demo_activities_page.dart +++ b/lib/modules/demo_module/demo_activities_page.dart @@ -3,6 +3,7 @@ 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/generic_attachment_model.dart'; import 'package:test_sa/views/widgets/images/files_list.dart'; import '../../controllers/api_routes/urls.dart'; @@ -57,10 +58,17 @@ class DemoActivitiesPage extends StatelessWidget { "Attachments".addTranslation, style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), - FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e ?? '') ?? '').toList() ?? []), + FilesList( + showAsListView: true, + types: allAttachments + .whereType() + .where((e) => e.documentType != null && e.documentType!.name!.isNotEmpty) + .map((toElement) => toElement.documentType!.name!) + .toList(), + images: allAttachments.map((e) => URLs.getFileUrl(e ?? '') ?? '').toList()), ], ], - ).toShadowContainer(context, borderRadius: 14, backgroundColor: Color(0xffF4F8FC), showShadow: false), + ).toShadowContainer(context, borderRadius: 14, backgroundColor: const Color(0xffF4F8FC), showShadow: false), 16.height, Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/modules/incident_module/incident_detail_page.dart b/lib/modules/incident_module/incident_detail_page.dart index 88ebd029..d54f4b4d 100644 --- a/lib/modules/incident_module/incident_detail_page.dart +++ b/lib/modules/incident_module/incident_detail_page.dart @@ -71,7 +71,14 @@ class IncidentDetailPage extends StatelessWidget { if (allAttachments.isNotEmpty) ...[ const Divider().defaultStyle(context), InfoHeader16Widget("Attachments".addTranslation), - FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), + FilesList( + showAsListView: true, + types: allAttachments + .whereType() + .where((e) => e.documentType != null && e.documentType!.name!.isNotEmpty) + .map((toElement) => toElement.documentType!.name!) + .toList(), + images: allAttachments.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), ], ], ).toShadowContainer(context), diff --git a/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart b/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart index 5e0a8a9b..94a52200 100644 --- a/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart +++ b/lib/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart @@ -118,7 +118,10 @@ class _EquipmentInternalAuditDetailPageState extends State URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), + FilesList( + showAsListView: true, + types: allAttachments + .whereType() + .where((e) => e.documentType != null && e.documentType!.name!.isNotEmpty) + .map((toElement) => toElement.documentType!.name!) + .toList(), + images: allAttachments.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), ], ], ).paddingAll(0).toShadowContainer(context), diff --git a/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_detail_page.dart b/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_detail_page.dart index a473a75a..dda1f510 100644 --- a/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_detail_page.dart +++ b/lib/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_detail_page.dart @@ -117,7 +117,14 @@ class _SystemInternalAuditDetailPageState extends State URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), + FilesList( + showAsListView: true, + types: allAttachments + .whereType() + .where((e) => e.documentType != null && e.documentType!.name!.isNotEmpty) + .map((toElement) => toElement.documentType!.name!) + .toList(), + images: allAttachments.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), ], ], ).paddingAll(0).toShadowContainer(context), diff --git a/lib/modules/loan_module/pages/loan_equipment_detail_page.dart b/lib/modules/loan_module/pages/loan_equipment_detail_page.dart index 8a043dbd..49800d56 100644 --- a/lib/modules/loan_module/pages/loan_equipment_detail_page.dart +++ b/lib/modules/loan_module/pages/loan_equipment_detail_page.dart @@ -97,7 +97,14 @@ class _LoanEquipmentDetailPageState extends State { if (allAttachments.isNotEmpty) ...[ const Divider().defaultStyle(context), InfoHeader16Widget("Attachments".addTranslation), - FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), + FilesList( + showAsListView: true, + types: allAttachments + .whereType() + .where((e) => e.documentType != null && e.documentType!.name!.isNotEmpty) + .map((toElement) => toElement.documentType!.name!) + .toList(), + images: allAttachments.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), ], ], ).toShadowContainer(context) 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 b499f20b..29eb131f 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 @@ -6,6 +6,7 @@ import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/generic_attachment_model.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.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'; @@ -103,7 +104,14 @@ class _MedicalGasInspectionDetailPageState extends State URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), + FilesList( + showAsListView: true, + types: model!.deliveryNoteAttachmentDto + .whereType() + .where((e) => e.documentType != null && e.documentType!.name!.isNotEmpty) + .map((toElement) => toElement.documentType!.name!) + .toList(), + images: model!.deliveryNoteAttachmentDto.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), ], ], ).toShadowContainer(context,), 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 82d36af0..6e683453 100644 --- a/lib/modules/tm_module/device_transfer/device_transfer_details.dart +++ b/lib/modules/tm_module/device_transfer/device_transfer_details.dart @@ -10,6 +10,7 @@ import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/helper/utils.dart'; import 'package:test_sa/models/device/device_transfer.dart'; +import 'package:test_sa/models/generic_attachment_model.dart'; import 'package:test_sa/modules/cx_module/chat/chat_widget.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/views/widgets/bottom_sheets/rejection_reason_bottom_sheet.dart'; @@ -121,7 +122,14 @@ class _DeviceTransferDetailsState extends State { if (_attachments.isNotEmpty) ...[ const Divider().defaultStyle(context), InfoHeader16Widget("Attachments".addTranslation), - FilesList(images: _model?.assetTransferAttachments?.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), + FilesList( + showAsListView: true, + types: _model?.assetTransferAttachments!=null? + _model!.assetTransferAttachments!.whereType() + .where((e) => e.documentType != null && e.documentType!.name!.isNotEmpty) + .map((toElement) => toElement.documentType!.name!) + .toList():[], + images: _model?.assetTransferAttachments?.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []), ] ], ), 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 3dc4fb3b..cfd42605 100644 --- a/lib/modules/tm_module/gas_refill/gas_refill_details.dart +++ b/lib/modules/tm_module/gas_refill/gas_refill_details.dart @@ -238,7 +238,10 @@ class _GasRefillDetailsPageState extends State { if (_attachments.isNotEmpty) ...[ const Divider().defaultStyle(context), InfoHeader16Widget("Attachments".addTranslation), - FilesList(images: _model.gasRefillAttachments?.whereType().where((e) => e.name != null).map((e) => URLs.getFileUrl(e.name!) ?? '').toList() ?? []), + FilesList( + showAsListView: true, + types: _model.gasRefillAttachments?.whereType().where((e) => e.documentType != null && e.documentType!.name != null).map((toElement) => toElement.documentType!.name!).toList() ?? [], + images: _model.gasRefillAttachments?.whereType().where((e) => e.name != null).map((e) => URLs.getFileUrl(e.name!) ?? '').toList() ?? []), ], ], ).expanded, 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 1102c9b7..13ad69f8 100644 --- a/lib/modules/tm_module/tasks/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks/task_request_detail_view.dart @@ -160,7 +160,10 @@ class _TaskRequestDetailsViewState extends State { if (taskProvider.taskRequestModel?.taskJobAttachments?.isNotEmpty ?? false) ...[ const Divider().defaultStyle(context), InfoHeader16Widget("Attachments".addTranslation), - FilesList(images: taskProvider.taskRequestModel!.taskJobAttachments!.whereType().where((e) => e.name != null).map((toElement) => URLs.getFileUrl(toElement.name ?? '') ?? '').toList()), + FilesList( + showAsListView: true, + types: taskProvider.taskRequestModel!.taskJobAttachments!.whereType().where((e) => e.name != null).map((toElement) => toElement.documentType?.name ?? '').toList(), + images: taskProvider.taskRequestModel!.taskJobAttachments!.whereType().where((e) => e.name != null).map((toElement) => URLs.getFileUrl(toElement.name ?? '') ?? '').toList()), ], buildTechnicalComments(taskModel: taskModel), ], diff --git a/lib/modules/traf_module/traf_request_detail_page.dart b/lib/modules/traf_module/traf_request_detail_page.dart index 32c839f7..16796bdb 100644 --- a/lib/modules/traf_module/traf_request_detail_page.dart +++ b/lib/modules/traf_module/traf_request_detail_page.dart @@ -155,7 +155,14 @@ class _TrafRequestDetailPageState extends State { style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), // 8.height, - FilesList(images: _attachments.map((e) => URLs.getFileUrl(e.path ?? '') ?? '').toList() ?? []), + FilesList( + showAsListView: true, + types: _attachments + .whereType() + .where((e) => e.documentType != null && e.documentType!.name!.isNotEmpty) + .map((toElement) => toElement.documentType!.name!) + .toList(), + images: _attachments.map((e) => URLs.getFileUrl(e.path ?? '') ?? '').toList() ?? []), ], ], ).toShadowContainer(context); diff --git a/lib/views/widgets/equipment/asset_detail_page.dart b/lib/views/widgets/equipment/asset_detail_page.dart index 2d0e25b1..8144eb46 100644 --- a/lib/views/widgets/equipment/asset_detail_page.dart +++ b/lib/views/widgets/equipment/asset_detail_page.dart @@ -93,7 +93,7 @@ class _AssetDetailPageState extends State { TabBarView( children: [ AssetDetailsView(assetModel: assetModel!, onUpload: _upload), - const AssetDocumentHistoryView(), + AssetDocumentHistoryView(assetId: assetId!), ], ).expanded, ], diff --git a/lib/views/widgets/equipment/asset_details_view.dart b/lib/views/widgets/equipment/asset_details_view.dart index 8b86fcd5..a00ac9d9 100644 --- a/lib/views/widgets/equipment/asset_details_view.dart +++ b/lib/views/widgets/equipment/asset_details_view.dart @@ -321,7 +321,14 @@ class _AssetDetailsViewState extends State with SingleTickerPr opacity: attachmentAnimation, child: SlideTransition( position: attachmentSlideAnimation, - child: FilesList(images: attachments.map((toElement) => URLs.getFileUrl(toElement.name!) ?? '').toList()), + child: FilesList( + showAsListView: true, + types: attachments + .whereType() + .where((e) => e.documentType != null && e.documentType!.name!.isNotEmpty) + .map((toElement) => toElement.documentType!.name!) + .toList(), + images: attachments.map((toElement) => URLs.getFileUrl(toElement.name!) ?? '').toList()), ), ), ] diff --git a/lib/views/widgets/equipment/asset_document_history_view.dart b/lib/views/widgets/equipment/asset_document_history_view.dart index 68cc3b02..701824a2 100644 --- a/lib/views/widgets/equipment/asset_document_history_view.dart +++ b/lib/views/widgets/equipment/asset_document_history_view.dart @@ -1,29 +1,155 @@ +import 'dart:developer'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/controllers/providers/api/devices_provider.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/generic_attachment_model.dart'; import 'package:test_sa/views/widgets/images/files_list.dart'; +import 'package:test_sa/views/widgets/item_views/info_text_widget.dart'; +import 'package:test_sa/views/widgets/loaders/app_loading.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; -class AssetDocumentHistoryView extends StatelessWidget { - const AssetDocumentHistoryView({Key? key}) : super(key: key); +class AssetDocumentHistoryView extends StatefulWidget { + final int assetId; + + const AssetDocumentHistoryView({ + Key? key, + required this.assetId, + }) : super(key: key); + + @override + State createState() => _AssetDocumentHistoryViewState(); +} + +class _AssetDocumentHistoryViewState extends State { + late AssetProvider _assetProvider; + final ScrollController _scrollController = ScrollController(); + List _documents = []; + bool _isLoading = false; + int _currentPage = 1; + int _totalRows = 0; + + @override + void initState() { + super.initState(); + _assetProvider = context.read(); + _scrollController.addListener(_onScroll); + _loadDocuments(); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + void _onScroll() { + final pixels = _scrollController.position.pixels; + final maxScroll = _scrollController.position.maxScrollExtent; + if (pixels >= maxScroll * 0.8) { + if (!_isLoading && _hasMore) { + _loadMore(); + } + } + } + + bool get _hasMore => _documents.length < _totalRows; + + Future _loadDocuments({bool refresh = false, bool loadMore = false}) async { + if (_isLoading) return; + setState(() { + _isLoading = true; + if (refresh) { + _documents.clear(); + _currentPage = 1; + } + if (loadMore) { + _currentPage++; + } + }); + try { + final result = await _assetProvider.getAssetDocumentHistory( + assetId: widget.assetId, + pageNumber: _currentPage, + ); + if (mounted) { + setState(() { + if (refresh) { + _documents = result.documents; + } else { + _documents.addAll(result.documents); + } + _totalRows = result.totalRows; + _isLoading = false; + }); + } + } catch (e) { + log('Error loading documents: $e'); + if (mounted) { + setState(() { + _isLoading = false; + }); + } + } + } + + Future _loadMore() async { + await _loadDocuments(loadMore: true); + } + + Future _onRefresh() async { + await _loadDocuments(refresh: true); + } @override Widget build(BuildContext context) { - final List dummyDocuments = [ - 'https://picsum.photos/200/300?random=1', - 'https://picsum.photos/200/300?random=2', - 'https://picsum.photos/200/300?random=3', - 'https://picsum.photos/200/300?random=4', - 'https://picsum.photos/200/300?random=5', - ]; - - return SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: FilesList( - showAsListView: true, - showDownloadButton: true, - images: dummyDocuments, - separator: const Divider().defaultStyle(context), - ).toShadowContainer(context), + if (_isLoading && _documents.isEmpty) { + return const Center(child: ALoading()); + } + if (_documents.isEmpty && !_isLoading) { + return const Center(child: NoDataFound()); + } + return RefreshIndicator( + onRefresh: _onRefresh, + child: ListView( + controller: _scrollController, + padding: EdgeInsets.symmetric( + horizontal: 16.toScreenWidth, + ), + children: [ + FilesList( + showAsListView: true, + showDownloadButton: true, + itemGap: 12.toScreenHeight, + images: _documents + .map((doc) { + return URLs.getFileUrl(doc.name) ?? ''; + }).where((url) => url.isNotEmpty).toList(), + types: _documents.map((doc) { + final attachmentType = doc.documentType?.name ?? ''; + return attachmentType.isNotEmpty ? attachmentType : 'Asset Guide'; + }).toList(), + ).toShadowContainer(context), + if (_isLoading && _documents.isNotEmpty&&_hasMore) ...[ + 16.height, + Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const ALoading(), + 8.height, + const InfoTextLabelWidget(label: 'Loading more...'), + ], + ), + ), + ), + ], + ], + ), ); } } diff --git a/lib/views/widgets/images/files_list.dart b/lib/views/widgets/images/files_list.dart index 8bfba4da..f1af8d30 100644 --- a/lib/views/widgets/images/files_list.dart +++ b/lib/views/widgets/images/files_list.dart @@ -1,5 +1,4 @@ import 'dart:io'; - import 'package:ellipsized_text/ellipsized_text.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -10,7 +9,6 @@ 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/helper/utils.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/views/widgets/loaders/image_loader.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -24,7 +22,7 @@ class FilesList extends StatelessWidget { final double? itemGap; final Widget? separator; - const FilesList({Key? key, this.separator, this.images = const [], this.types = const [], this.padding, this.showAsListView = false, this.showDownloadButton = false, this.itemGap}) + const FilesList({Key? key, this.separator, this.images = const [], this.types = const [], this.padding, required this.showAsListView, this.showDownloadButton = false, this.itemGap}) : super(key: key); @override @@ -75,8 +73,9 @@ class FilesList extends StatelessWidget { children: [ EllipsizedText( images[itemIndex].split("/").last, - style: AppTextStyles.bodyText.copyWith(color: AppColor.textStyleColor(context)), - type: EllipsisType.start, // or EllipsisType.middle + style: AppTextStyles.bodyText2.copyWith(color: AppColor.labelTextStyleColor(context)), + // style: AppTextStyles.bodyText.copyWith(color: AppColor.textStyleColor(context)), + type: EllipsisType.start, ), 2.height, RichText(