From 90aabebe12d482cc2e62b70778db087d52460402 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 31 Aug 2023 09:34:03 +0300 Subject: [PATCH 01/16] calling api to get complete data for service request to show attachments and voice notes. & improvements --- .../api/service_requests_provider.dart | 14 ++++++++++ .../service_request/service_request.dart | 6 +++-- .../pages/user/requests/create_request.dart | 26 +++++++++++++++++-- .../widgets/images/multi_image_picker.dart | 3 +++ .../images/multi_image_picker_item.dart | 9 +++++-- 5 files changed, 52 insertions(+), 6 deletions(-) diff --git a/lib/controllers/providers/api/service_requests_provider.dart b/lib/controllers/providers/api/service_requests_provider.dart index 41e749c0..21b690a7 100644 --- a/lib/controllers/providers/api/service_requests_provider.dart +++ b/lib/controllers/providers/api/service_requests_provider.dart @@ -614,6 +614,20 @@ class ServiceRequestsProvider extends ChangeNotifier { } } + Future getServiceRequestObjectById({@required String requestId}) async { + try { + Response response = await ApiManager.instance.get(URLs.getServiceRequestById + "?callRequestId=$requestId"); + if (response.statusCode >= 200 && response.statusCode < 300) { + return ServiceRequest.fromJson(json.decode(response.body)["data"]); + } else { + return null; + } + } catch (error) { + print(error); + return null; + } + } + Future> getServiceRequestById({@required String requestId}) async { Response response; try { diff --git a/lib/models/service_request/service_request.dart b/lib/models/service_request/service_request.dart index 68a55eb8..78828a29 100644 --- a/lib/models/service_request/service_request.dart +++ b/lib/models/service_request/service_request.dart @@ -84,9 +84,11 @@ class ServiceRequest { factory ServiceRequest.fromJson(Map parsedJson) { List images = []; + print(parsedJson["attachmentsCallRequest"]); if (parsedJson["attachmentsCallRequest"] != null) if (parsedJson["attachmentsCallRequest"] is List) { List list = parsedJson["attachmentsCallRequest"]; - images = list.map((e) => URLs.getFileUrl(e["attachmentsCallRequest"])).toList(); + images = list.map((e) => URLs.getFileUrl(e["name"])).toList(); + print(images); } return ServiceRequest( id: parsedJson["id"].toString(), @@ -123,7 +125,7 @@ class ServiceRequest { reviewComment: parsedJson["reviewComment"], type: Lookup.fromJson(parsedJson['typeofRequest']), defectType: Lookup.fromJson(parsedJson['defectType']), - loanAvailability: Lookup.fromJson(parsedJson['loanAvailability']), + loanAvailability: Lookup.fromJson(parsedJson['loanAvailablity']), firstAction: Lookup.fromJson(parsedJson['firstAction']), requestedThrough: Lookup.fromJson(parsedJson['requestedThrough']), priority: Lookup.fromJson(parsedJson['priority']), diff --git a/lib/views/pages/user/requests/create_request.dart b/lib/views/pages/user/requests/create_request.dart index dddba5a1..74000354 100644 --- a/lib/views/pages/user/requests/create_request.dart +++ b/lib/views/pages/user/requests/create_request.dart @@ -74,14 +74,33 @@ class CreateRequestPageState extends State { _device = _serviceRequest.device; _deviceImages.addAll(_serviceRequest.devicePhotos.map((e) => File(e)).toList()); _showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit"; + if (_showDatePicker && _serviceRequest.visitDate != null) { + _dateTime = DateTime.tryParse(_serviceRequest.visitDate); + } } else { _serviceRequest = ServiceRequest(); } + _isLoading = true; super.initState(); } getServiceRequestById(String id) async { - await _serviceRequestsProvider.getServiceRequestById(requestId: id) ?? ""; + try { + ServiceRequest request = await _serviceRequestsProvider.getServiceRequestObjectById(requestId: id) ?? ""; + _serviceRequest = request; + _device = _serviceRequest.device; + _deviceImages.addAll(_serviceRequest.devicePhotos.map((e) { + return File(e); + }).toList()); + _showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit"; + if (_showDatePicker && _serviceRequest.visitDate != null) { + _dateTime = DateTime.tryParse(_serviceRequest.visitDate); + } + _isLoading = false; + } catch (ex) { + _isLoading = false; + } + setState(() {}); } @override @@ -101,8 +120,11 @@ class CreateRequestPageState extends State { _height = MediaQuery.of(context).size.height; _userProvider = Provider.of(context); _settingProvider = Provider.of(context); - _serviceRequestsProvider = Provider.of(context); _subtitle = AppLocalization.of(context).subtitle; + if (_serviceRequestsProvider == null) { + _serviceRequestsProvider = Provider.of(context); + getServiceRequestById(widget.serviceRequest.id); + } return Scaffold( key: _scaffoldKey, diff --git a/lib/views/widgets/images/multi_image_picker.dart b/lib/views/widgets/images/multi_image_picker.dart index 84ad643e..a19d3d56 100644 --- a/lib/views/widgets/images/multi_image_picker.dart +++ b/lib/views/widgets/images/multi_image_picker.dart @@ -130,6 +130,9 @@ class _MultiFilesPickerState extends State with TickerProvider return MultiFilesPickerItem( file: _image, onRemoveTap: (image) { + if (!widget.enabled) { + return; + } widget.files.remove(image); setState(() {}); }, diff --git a/lib/views/widgets/images/multi_image_picker_item.dart b/lib/views/widgets/images/multi_image_picker_item.dart index 8ec971d8..fea9ce6f 100644 --- a/lib/views/widgets/images/multi_image_picker_item.dart +++ b/lib/views/widgets/images/multi_image_picker_item.dart @@ -23,7 +23,7 @@ class MultiFilesPickerItem extends StatelessWidget { boxShadow: [BoxShadow(color: isImage ? Colors.black38 : AColors.cyan.withOpacity(0.5), blurRadius: 2)], image: DecorationImage( image: isImage - ? FileImage(file) + ? (_isLocalUrl(file.path) ? FileImage(file) : NetworkImage(file.path)) : AssetImage("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png"), fit: BoxFit.cover, ), @@ -39,7 +39,7 @@ class MultiFilesPickerItem extends StatelessWidget { body: SafeArea( child: Stack( children: [ - Center(child: isImage ? InteractiveViewer(child: Image(image: FileImage(file))) : const SizedBox.shrink()), + Center(child: isImage ? InteractiveViewer(child: Image(image: (_isLocalUrl(file.path) ? FileImage(file) : NetworkImage(file.path)))) : const SizedBox.shrink()), const ABackButton(), ], ), @@ -69,4 +69,9 @@ class MultiFilesPickerItem extends StatelessWidget { ), ); } + + bool _isLocalUrl(String url) { + if (url?.isEmpty != false) return false; + return url.startsWith("/") || url.startsWith("file://") || url.substring(1).startsWith(':\\'); + } } From 19cd20c64576360831c263304cd9521446e5368e Mon Sep 17 00:00:00 2001 From: zaid_daoud Date: Thu, 31 Aug 2023 11:02:23 +0300 Subject: [PATCH 02/16] 1- Fix Vendor Engineer Issue 2- Add Install Qty & Return Qty --- .../api/service_requests_provider.dart | 2 +- lib/models/part.dart | 23 ++++--- .../service_request/search_work_order.dart | 9 ++- .../sub_work_order_details.dart | 9 +-- .../create_sub_workorder_page.dart | 10 ++- .../spare_parts_details_bottom_sheet.dart | 61 +++++++++++++++---- .../work_order_details_bottom_sheet.dart | 22 ++++--- .../parts/auto_complete_parts_field.dart | 6 +- lib/views/widgets/parts/part_item.dart | 11 ++-- .../widgets/pentry/pentry_pm_kit_form.dart | 2 +- 10 files changed, 103 insertions(+), 52 deletions(-) diff --git a/lib/controllers/providers/api/service_requests_provider.dart b/lib/controllers/providers/api/service_requests_provider.dart index 41e749c0..e62754f7 100644 --- a/lib/controllers/providers/api/service_requests_provider.dart +++ b/lib/controllers/providers/api/service_requests_provider.dart @@ -339,7 +339,7 @@ class ServiceRequestsProvider extends ChangeNotifier { ?.map( (p) => { "id": p.reportPartID ?? 0, - "sparePart": {"id": p.id, "partNo": p.code, "partName": p.name}, + "sparePart": {"id": p.id, "partNo": p.partNo, "partName": p.partName}, "qty": p.quantity }, ) diff --git a/lib/models/part.dart b/lib/models/part.dart index 176811cc..9f8b0cad 100644 --- a/lib/models/part.dart +++ b/lib/models/part.dart @@ -1,23 +1,28 @@ class Part { int id; int reportPartID; - String code; - String name; + String partNo; + String partName; int quantity; + double returnQty, installQty; Part({ this.id, this.reportPartID, - this.code, - this.name, + this.partNo, + this.partName, this.quantity = 1, + this.installQty, + this.returnQty, }); Map toJson() { return { "id": reportPartID ?? 0, - "sparePart": {"id": id, "partNo": code, "partName": name}, - "qty": quantity + "sparePart": {"id": id, "partNo": partNo, "partName": partName}, + "qty": quantity, + if (returnQty != null) "returnQty": returnQty, + if (installQty != null) "installQty": installQty, }; } @@ -25,9 +30,11 @@ class Part { return Part( id: parsedJson["id"], reportPartID: reportJson != null ? reportJson["id"] : null, - code: parsedJson["partNo"], - name: parsedJson["partName"], + partNo: parsedJson["partNo"], + partName: parsedJson["partName"], quantity: reportJson != null ? (reportJson["qty"] ?? 1).toInt() : 1, + returnQty: parsedJson["returnQty"], + installQty: parsedJson["installQty"], ); } } diff --git a/lib/models/service_request/search_work_order.dart b/lib/models/service_request/search_work_order.dart index 564b7dd6..e4f9fa08 100644 --- a/lib/models/service_request/search_work_order.dart +++ b/lib/models/service_request/search_work_order.dart @@ -1,4 +1,5 @@ import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/models/service_request/sub_work_order_details.dart'; import 'package:test_sa/models/timer_model.dart'; import '../fault_description.dart'; @@ -163,7 +164,6 @@ class SearchWorkOrder { Lookup currentSituation; Lookup repairLocation; Lookup reason; - // String startofWorkTime; // String endofWorkTime; num workingHours; @@ -321,6 +321,13 @@ class SearchWorkOrder { } return map; } + + void copyDetails(SubWorkOrderDetails subWoDetails) { + equipmentStatus = subWoDetails.equipmentStatus; + reason = subWoDetails.reason; + faultDescription = subWoDetails.faultDescription; + sparePartsWorkOrders = subWoDetails.sparePartsWorkOrders; + } } class WoParentDto { diff --git a/lib/models/service_request/sub_work_order_details.dart b/lib/models/service_request/sub_work_order_details.dart index e47d93f8..4d0e553d 100644 --- a/lib/models/service_request/sub_work_order_details.dart +++ b/lib/models/service_request/sub_work_order_details.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/service_request/search_work_order.dart'; @@ -7,14 +9,12 @@ class SubWorkOrderDetails { Lookup equipmentStatus; Lookup reason; FaultDescription faultDescription; - // Lookup loanAvailablity; List sparePartsWorkOrders; SubWorkOrderDetails({ this.equipmentStatus, this.reason, this.faultDescription, - // this.loanAvailablity, this.sparePartsWorkOrders, }); @@ -22,7 +22,7 @@ class SubWorkOrderDetails { if (json['equipmentStatus'] != null) equipmentStatus = Lookup.fromJson(json['equipmentStatus']); if (json['reason'] != null) reason = Lookup.fromJson(json['reason']); if (json['faultDescription'] != null) faultDescription = FaultDescription.fromJson(json['faultDescription']); - // loanAvailablity = Lookup.fromJson(json['loanAvailablity']); + log("supplier : ${json['supplier']}"); if (json['sparePartsWorkOrders'] != null) { sparePartsWorkOrders = []; json['sparePartsWorkOrders'].forEach((v) { @@ -35,14 +35,12 @@ class SubWorkOrderDetails { Lookup equipmentStatus, Lookup reason, FaultDescription faultDescription, - // Lookup loanAvailablity, List sparePartsWorkOrders, }) => SubWorkOrderDetails( equipmentStatus: equipmentStatus ?? this.equipmentStatus, reason: reason ?? this.reason, faultDescription: faultDescription ?? this.faultDescription, - // loanAvailablity: loanAvailablity ?? this.loanAvailablity, sparePartsWorkOrders: sparePartsWorkOrders ?? this.sparePartsWorkOrders, ); @@ -51,7 +49,6 @@ class SubWorkOrderDetails { map['equipmentStatus'] = equipmentStatus?.toMap(); map['reason'] = reason?.toMap(); map['faultDescription'] = faultDescription?.toJson(); - // map['loanAvailablity'] = loanAvailablity?.toMap(); if (sparePartsWorkOrders?.isNotEmpty ?? false) { map['sparePartsWorkOrders'] = sparePartsWorkOrders?.map((e) => e.toJson())?.toList(); } diff --git a/lib/views/pages/sub_workorder/create_sub_workorder_page.dart b/lib/views/pages/sub_workorder/create_sub_workorder_page.dart index 7511fa91..129790b7 100644 --- a/lib/views/pages/sub_workorder/create_sub_workorder_page.dart +++ b/lib/views/pages/sub_workorder/create_sub_workorder_page.dart @@ -75,15 +75,13 @@ class _CreateSubWorkOrderPageState extends State { assetTypesProvider = Provider.of(context, listen: false); _callRequestForWorkOrder = await serviceRequestProvider.getCallRequestForWorkOrder(callId: widget.workOrder.callRequest.id.toString()); if (_subWorkOrders?.parentWOId != null) { + print(_subWorkOrders?.parentWOId.toString()); final subWoDetails = await serviceRequestProvider.getSubWorkOrderDetails(parentId: _subWorkOrders?.parentWOId.toString()); - _subWorkOrders.equipmentStatus = subWoDetails.equipmentStatus; - _subWorkOrders.reason = subWoDetails.reason; - _serviceReportReason = _subWorkOrders.reason; - _subWorkOrders.faultDescription = subWoDetails.faultDescription; - _subWorkOrders.sparePartsWorkOrders = subWoDetails.sparePartsWorkOrders; + _subWorkOrders.copyDetails(subWoDetails); _serviceReport.equipmentStatus = subWoDetails.equipmentStatus; _serviceReport.reason = subWoDetails.reason; _serviceReport.faultDescription = subWoDetails.faultDescription; + _serviceReportReason = _subWorkOrders.reason; } await assetTypesProvider.getTypes(); _subWorkOrders?.assetType = assetTypesProvider.statuses?.firstWhere( @@ -213,7 +211,7 @@ class _CreateSubWorkOrderPageState extends State { onSelect: (status) { _serviceReportReason = status; _subWorkOrders.reason = status; - setState(() {}); + // setState(() {}); }, ), ], diff --git a/lib/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart b/lib/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart index 661d0cbe..f9bee3ec 100644 --- a/lib/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart +++ b/lib/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart @@ -6,6 +6,7 @@ import 'package:test_sa/views/app_style/colors.dart'; import '../../../controllers/localization/localization.dart'; import '../../../models/subtitle.dart'; import '../../app_style/sizing.dart'; +import '../../widgets/app_text_form_field.dart'; import '../../widgets/buttons/app_button.dart'; import '../../widgets/parts/auto_complete_parts_field.dart'; import '../../widgets/parts/part_item.dart'; @@ -101,7 +102,7 @@ class _SparePartsBottomSheetState extends State { _workOrder.sparePartsWorkOrders.add(SparePartsWorkOrders( id: part.reportPartID, qty: part.quantity, - sparePart: SparePart(id: part.id, partName: part.name, partNo: part.code), + sparePart: SparePart(id: part.id, partName: part.partName, partNo: part.partNo), )); }, ), @@ -129,19 +130,55 @@ class _SparePartsBottomSheetState extends State { Part part = Part( id: spare.sparePart?.id, reportPartID: spare.id, - code: spare.sparePart?.partNo, - name: spare.sparePart?.partName, + partNo: spare.sparePart?.partNo, + partName: spare.sparePart?.partName, quantity: spare.qty?.toInt(), + installQty: spare.installQty, + returnQty: spare.returnQty, ); - return PartItem( - part: part, - onEdit: (qty) { - spare.qty = qty; - }, - onDelete: (part) { - _workOrder.sparePartsWorkOrders.remove(spare); - setState(() {}); - }, + return Column( + children: [ + PartItem( + part: part, + onEdit: (qty) { + spare.qty = qty; + }, + onDelete: (part) { + _workOrder.sparePartsWorkOrders.remove(spare); + setState(() {}); + }, + ), + if (_workOrder.calllastSituation?.name?.toLowerCase()?.contains("part installation") ?? false) + Row( + children: [ + Expanded( + child: ATextFormField( + initialValue: part?.returnQty?.toString(), + hintText: "Return Quantity", + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleMedium, + textInputType: TextInputType.number, + onSaved: (value) { + part.returnQty = num.tryParse(value); + }, + ), + ), + const SizedBox(width: 8), + Expanded( + child: ATextFormField( + initialValue: part?.installQty?.toString(), + hintText: "Install Quantity", + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleMedium, + textInputType: TextInputType.number, + onSaved: (value) { + part.installQty = num.tryParse(value); + }, + ), + ), + ], + ), + ], ); }, ), diff --git a/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart b/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart index fb595275..b8f1ebd4 100644 --- a/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart +++ b/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:provider/provider.dart'; +import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/service_request/search_work_order.dart'; import 'package:test_sa/models/subtitle.dart'; import 'package:test_sa/views/widgets/app_text_form_field.dart'; @@ -33,10 +34,10 @@ class _WorkOrderDetailsBottomSheetState extends State { }, itemBuilder: (context, part) { return ListTile( - title: Text(part.code), + title: Text(part.partNo), subtitle: Text( - part.name, + part.partName, style: Theme.of(context).textTheme.caption, ), ); @@ -79,7 +79,7 @@ class _AutoCompletePartsFieldState extends State { if (widget.clearAfterPick) { _controller.clear(); } else { - _controller.text = part.code; + _controller.text = part.partNo; } widget.onPick(part); }, diff --git a/lib/views/widgets/parts/part_item.dart b/lib/views/widgets/parts/part_item.dart index 87fcec03..0d98b0e0 100644 --- a/lib/views/widgets/parts/part_item.dart +++ b/lib/views/widgets/parts/part_item.dart @@ -21,7 +21,8 @@ class _PartItemState extends State { return Column( children: [ const Divider(), - Row(crossAxisAlignment: CrossAxisAlignment.start, + Row( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( @@ -31,7 +32,7 @@ class _PartItemState extends State { children: [ Expanded( child: Text( - widget?.part?.code ?? "", + widget?.part?.partNo ?? "", style: Theme.of(context).textTheme.bodyText1.copyWith(fontSize: 12, fontWeight: FontWeight.bold), ), ), @@ -76,16 +77,16 @@ class _PartItemState extends State { ), ], ), - widget.part.name == null + widget.part.partName == null ? const SizedBox.shrink() : Text( - widget.part.name, + widget.part.partName, style: Theme.of(context).textTheme.caption.copyWith(fontSize: 11, fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis, ), - // Row(crossAxisAlignment: ,) + // Row(crossAxisAlignment: ,) ], ), ), diff --git a/lib/views/widgets/pentry/pentry_pm_kit_form.dart b/lib/views/widgets/pentry/pentry_pm_kit_form.dart index 3fb154d7..f072bc84 100644 --- a/lib/views/widgets/pentry/pentry_pm_kit_form.dart +++ b/lib/views/widgets/pentry/pentry_pm_kit_form.dart @@ -74,7 +74,7 @@ class _PentryPMKitFormState extends State { clearAfterPick: false, initialValue: (model.itemCode?.name ?? "").toString(), onPick: (part) { - model.itemCode = Lookup(id: part.id, name: part.code); + model.itemCode = Lookup(id: part.id, name: part.partNo); }, ), const SizedBox( From b908322f066278beb66f47f7fe70441b24a24cb9 Mon Sep 17 00:00:00 2001 From: zaid_daoud Date: Thu, 31 Aug 2023 11:12:08 +0300 Subject: [PATCH 03/16] 1- [WO Details] Get Vendor Name And Engineer According To Last Status --- .../service_request/search_work_order.dart | 2 ++ .../sub_work_order_details.dart | 22 ++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/models/service_request/search_work_order.dart b/lib/models/service_request/search_work_order.dart index e4f9fa08..4294f499 100644 --- a/lib/models/service_request/search_work_order.dart +++ b/lib/models/service_request/search_work_order.dart @@ -327,6 +327,8 @@ class SearchWorkOrder { reason = subWoDetails.reason; faultDescription = subWoDetails.faultDescription; sparePartsWorkOrders = subWoDetails.sparePartsWorkOrders; + suppEngineerWorkOrders = subWoDetails.suppEngineerWorkOrders; + supplier = subWoDetails.supplier; } } diff --git a/lib/models/service_request/sub_work_order_details.dart b/lib/models/service_request/sub_work_order_details.dart index 4d0e553d..6e58d34c 100644 --- a/lib/models/service_request/sub_work_order_details.dart +++ b/lib/models/service_request/sub_work_order_details.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/service_request/search_work_order.dart'; @@ -10,25 +8,35 @@ class SubWorkOrderDetails { Lookup reason; FaultDescription faultDescription; List sparePartsWorkOrders; + List suppEngineerWorkOrders; + SupplierModel supplier; SubWorkOrderDetails({ this.equipmentStatus, this.reason, this.faultDescription, this.sparePartsWorkOrders, + this.supplier, + this.suppEngineerWorkOrders, }); SubWorkOrderDetails.fromJson(dynamic json) { if (json['equipmentStatus'] != null) equipmentStatus = Lookup.fromJson(json['equipmentStatus']); if (json['reason'] != null) reason = Lookup.fromJson(json['reason']); if (json['faultDescription'] != null) faultDescription = FaultDescription.fromJson(json['faultDescription']); - log("supplier : ${json['supplier']}"); + if (json['supplier'] != null) supplier = SupplierModel.fromJson(json['supplier']); if (json['sparePartsWorkOrders'] != null) { sparePartsWorkOrders = []; json['sparePartsWorkOrders'].forEach((v) { sparePartsWorkOrders.add(SparePartsWorkOrders.fromJson(v)); }); } + if (json['suppEngineerWorkOrders'] != null) { + suppEngineerWorkOrders = []; + json['suppEngineerWorkOrders'].forEach((v) { + suppEngineerWorkOrders.add(SuppEngineerWorkOrders.fromJson(v)); + }); + } } SubWorkOrderDetails copyWith({ @@ -36,12 +44,16 @@ class SubWorkOrderDetails { Lookup reason, FaultDescription faultDescription, List sparePartsWorkOrders, + SupplierModel supplier, + SuppEngineerWorkOrders suppEngineerWorkOrders, }) => SubWorkOrderDetails( equipmentStatus: equipmentStatus ?? this.equipmentStatus, reason: reason ?? this.reason, faultDescription: faultDescription ?? this.faultDescription, sparePartsWorkOrders: sparePartsWorkOrders ?? this.sparePartsWorkOrders, + supplier: supplier ?? this.supplier, + suppEngineerWorkOrders: suppEngineerWorkOrders ?? this.suppEngineerWorkOrders, ); Map toJson() { @@ -52,6 +64,10 @@ class SubWorkOrderDetails { if (sparePartsWorkOrders?.isNotEmpty ?? false) { map['sparePartsWorkOrders'] = sparePartsWorkOrders?.map((e) => e.toJson())?.toList(); } + if (suppEngineerWorkOrders?.isNotEmpty ?? false) { + map['suppEngineerWorkOrders'] = suppEngineerWorkOrders?.map((e) => e.toJson())?.toList(); + } + map['supplier'] = supplier?.toJson(); return map; } } From 862451543370b67e99f5a4fdaf9511208a618252 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 31 Aug 2023 11:42:03 +0300 Subject: [PATCH 04/16] local & network files opener added. --- lib/views/pages/user/requests/create_request.dart | 7 +++++-- .../widgets/images/multi_image_picker_item.dart | 15 +++++++++++++-- pubspec.lock | 8 ++++++++ pubspec.yaml | 1 + 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/views/pages/user/requests/create_request.dart b/lib/views/pages/user/requests/create_request.dart index 74000354..96321251 100644 --- a/lib/views/pages/user/requests/create_request.dart +++ b/lib/views/pages/user/requests/create_request.dart @@ -80,7 +80,7 @@ class CreateRequestPageState extends State { } else { _serviceRequest = ServiceRequest(); } - _isLoading = true; + super.initState(); } @@ -123,7 +123,10 @@ class CreateRequestPageState extends State { _subtitle = AppLocalization.of(context).subtitle; if (_serviceRequestsProvider == null) { _serviceRequestsProvider = Provider.of(context); - getServiceRequestById(widget.serviceRequest.id); + if (widget.serviceRequest?.id != null) { + _isLoading = true; + getServiceRequestById(widget.serviceRequest.id); + } } return Scaffold( diff --git a/lib/views/widgets/images/multi_image_picker_item.dart b/lib/views/widgets/images/multi_image_picker_item.dart index fea9ce6f..5cddee73 100644 --- a/lib/views/widgets/images/multi_image_picker_item.dart +++ b/lib/views/widgets/images/multi_image_picker_item.dart @@ -1,9 +1,13 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:open_file/open_file.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/views/app_style/colors.dart'; import 'package:test_sa/views/app_style/sizing.dart'; import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; +import 'package:url_launcher/url_launcher.dart'; class MultiFilesPickerItem extends StatelessWidget { final File file; @@ -31,7 +35,7 @@ class MultiFilesPickerItem extends StatelessWidget { ), child: MaterialButton( padding: EdgeInsets.zero, - onPressed: () { + onPressed: () async { if (isImage) { Navigator.of(context).push( MaterialPageRoute( @@ -39,7 +43,7 @@ class MultiFilesPickerItem extends StatelessWidget { body: SafeArea( child: Stack( children: [ - Center(child: isImage ? InteractiveViewer(child: Image(image: (_isLocalUrl(file.path) ? FileImage(file) : NetworkImage(file.path)))) : const SizedBox.shrink()), + InteractiveViewer(child: Image(image: (_isLocalUrl(file.path) ? FileImage(file) : NetworkImage(file.path)))).center, const ABackButton(), ], ), @@ -47,6 +51,13 @@ class MultiFilesPickerItem extends StatelessWidget { ), ), ); + } else if (_isLocalUrl(file.path)) { + OpenFile.open(file.path); + } else { + if (!await launchUrl(Uri.parse(file.path), mode: LaunchMode.externalApplication)) { + Fluttertoast.showToast(msg: "UnExpected Error with file."); + throw Exception('Could not launch '); + } } }, child: Align( diff --git a/pubspec.lock b/pubspec.lock index 98397238..87291639 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -701,6 +701,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + open_file: + dependency: "direct main" + description: + name: open_file + sha256: a5a32d44acb7c899987d0999e1e3cbb0a0f1adebbf41ac813ec6d2d8faa0af20 + url: "https://pub.dev" + source: hosted + version: "3.3.2" package_info: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 4616c3d2..677ef09c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -64,6 +64,7 @@ dependencies: file_picker: ^5.2.5 record_mp3: ^2.1.0 path_provider: ^2.1.0 + open_file: ^3.3.2 dev_dependencies: flutter_test: From 8c018158ae62f3fdedebbba9e6732392626752d0 Mon Sep 17 00:00:00 2001 From: zaid_daoud Date: Thu, 31 Aug 2023 12:05:05 +0300 Subject: [PATCH 05/16] Vendors Will Be Visible If Previous Data API contains data --- .../sub_workorder/work_order_details_bottom_sheet.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart b/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart index b8f1ebd4..58ac107d 100644 --- a/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart +++ b/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart @@ -36,8 +36,10 @@ class _WorkOrderDetailsBottomSheetState extends State Date: Thu, 31 Aug 2023 12:18:10 +0300 Subject: [PATCH 06/16] Enhancement --- .../spare_parts_details_bottom_sheet.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart b/lib/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart index f9bee3ec..d8d47e5c 100644 --- a/lib/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart +++ b/lib/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart @@ -103,6 +103,8 @@ class _SparePartsBottomSheetState extends State { id: part.reportPartID, qty: part.quantity, sparePart: SparePart(id: part.id, partName: part.partName, partNo: part.partNo), + installQty: part.installQty, + returnQty: part.returnQty, )); }, ), @@ -154,12 +156,12 @@ class _SparePartsBottomSheetState extends State { Expanded( child: ATextFormField( initialValue: part?.returnQty?.toString(), - hintText: "Return Quantity", + labelText: "Return Quantity", textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleMedium, textInputType: TextInputType.number, onSaved: (value) { - part.returnQty = num.tryParse(value); + spare.returnQty = num.tryParse(value)?.toDouble(); }, ), ), @@ -167,12 +169,12 @@ class _SparePartsBottomSheetState extends State { Expanded( child: ATextFormField( initialValue: part?.installQty?.toString(), - hintText: "Install Quantity", + labelText: "Install Quantity", textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleMedium, textInputType: TextInputType.number, onSaved: (value) { - part.installQty = num.tryParse(value); + spare.installQty = num.tryParse(value)?.toDouble(); }, ), ), From 7d68d1db51273eef040612950381edc2d90c2f36 Mon Sep 17 00:00:00 2001 From: zaid_daoud Date: Thu, 31 Aug 2023 12:23:27 +0300 Subject: [PATCH 07/16] Fix --- lib/models/service_report.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/models/service_report.dart b/lib/models/service_report.dart index abf93564..020ef0a5 100644 --- a/lib/models/service_report.dart +++ b/lib/models/service_report.dart @@ -221,7 +221,7 @@ class ServiceReport { //quantity: parsedJson["nid"], travelingHours: parsedJson["travelingHours"], travelingExpense: parsedJson["travelingExpenses"], - visitDate: DateTime.tryParse(parsedJson["visitDate"]), + visitDate: DateTime.tryParse(parsedJson["visitDate"] ?? ""), returnToService: DateTime.tryParse(parsedJson["replacementDate"] ?? ""), workingHours: parsedJson["workingHours"], timer: TimerModel( From c9705e0b74b87c38e96a5b0547a6a10960f69037 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 31 Aug 2023 14:08:16 +0300 Subject: [PATCH 08/16] attachments improvements. --- .../api/service_requests_provider.dart | 1 - .../service_request/service_request.dart | 17 +- .../pages/user/requests/create_request.dart | 46 +- .../pages/user/requests/request_details.dart | 782 +++++++++--------- lib/views/widgets/images/files_list.dart | 73 ++ .../images/multi_image_picker_item.dart | 10 +- 6 files changed, 516 insertions(+), 413 deletions(-) create mode 100644 lib/views/widgets/images/files_list.dart diff --git a/lib/controllers/providers/api/service_requests_provider.dart b/lib/controllers/providers/api/service_requests_provider.dart index 21b690a7..3a035551 100644 --- a/lib/controllers/providers/api/service_requests_provider.dart +++ b/lib/controllers/providers/api/service_requests_provider.dart @@ -623,7 +623,6 @@ class ServiceRequestsProvider extends ChangeNotifier { return null; } } catch (error) { - print(error); return null; } } diff --git a/lib/models/service_request/service_request.dart b/lib/models/service_request/service_request.dart index 78828a29..98457831 100644 --- a/lib/models/service_request/service_request.dart +++ b/lib/models/service_request/service_request.dart @@ -1,5 +1,6 @@ import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/models/device/device.dart'; +import 'package:test_sa/models/fault_description.dart'; import 'package:test_sa/models/lookup.dart'; class ServiceRequest { @@ -27,8 +28,9 @@ class ServiceRequest { bool viewReport; String engineerMobile; String deviceModel; - String faultDescription; - String workPerformed; + FaultDescription faultDescription; + + //String workPerformed; String visitDate; DateTime nextVisitDate; String jobSheetNumber; @@ -68,7 +70,7 @@ class ServiceRequest { this.jobSheetNumber, this.visitDate, this.nextVisitDate, - this.workPerformed, + //this.workPerformed, this.reportID, this.defectType, this.priority, @@ -116,11 +118,16 @@ class ServiceRequest { viewReport: parsedJson["workOrder"] != null, deviceModel: parsedJson["asset"]["modelDefinition"]["modelName"], engineerMobile: parsedJson["assignedEmployee"] == null ? null : parsedJson["assignedEmployee"]["phone"], - faultDescription: parsedJson["workOrder"] != null ? parsedJson["workOrder"]["faultDescription"] : null, + faultDescription: parsedJson["workOrder"] != null + ? parsedJson["workOrder"]["faultDescription"] != null + ? FaultDescription.fromJson(parsedJson["workOrder"]["faultDescription"]) + : null + : null, + jobSheetNumber: parsedJson["workOrder"] != null ? parsedJson["workOrder"]["jobSheetNumber"] : null, visitDate: DateTime.tryParse(parsedJson["visitDate"] ?? "").toString().split(" ").first, nextVisitDate: DateTime.tryParse(parsedJson["nextVisitDate"] ?? ""), - workPerformed: parsedJson["workOrder"] != null ? parsedJson["workOrder"]["workPerformed"] : null, + //workPerformed: parsedJson["workOrder"] != null ? parsedJson["workOrder"]["workPerformed"] : null, device: Device.fromJson(parsedJson["asset"]), reviewComment: parsedJson["reviewComment"], type: Lookup.fromJson(parsedJson['typeofRequest']), diff --git a/lib/views/pages/user/requests/create_request.dart b/lib/views/pages/user/requests/create_request.dart index 96321251..db5b9fda 100644 --- a/lib/views/pages/user/requests/create_request.dart +++ b/lib/views/pages/user/requests/create_request.dart @@ -83,25 +83,25 @@ class CreateRequestPageState extends State { super.initState(); } - - getServiceRequestById(String id) async { - try { - ServiceRequest request = await _serviceRequestsProvider.getServiceRequestObjectById(requestId: id) ?? ""; - _serviceRequest = request; - _device = _serviceRequest.device; - _deviceImages.addAll(_serviceRequest.devicePhotos.map((e) { - return File(e); - }).toList()); - _showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit"; - if (_showDatePicker && _serviceRequest.visitDate != null) { - _dateTime = DateTime.tryParse(_serviceRequest.visitDate); - } - _isLoading = false; - } catch (ex) { - _isLoading = false; - } - setState(() {}); - } + // + // getServiceRequestById(String id) async { + // try { + // ServiceRequest request = await _serviceRequestsProvider.getServiceRequestObjectById(requestId: id) ?? ""; + // _serviceRequest = request; + // _device = _serviceRequest.device; + // _deviceImages.addAll(_serviceRequest.devicePhotos.map((e) { + // return File(e); + // }).toList()); + // _showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit"; + // if (_showDatePicker && _serviceRequest.visitDate != null) { + // _dateTime = DateTime.tryParse(_serviceRequest.visitDate); + // } + // _isLoading = false; + // } catch (ex) { + // _isLoading = false; + // } + // setState(() {}); + // } @override void dispose() { @@ -121,13 +121,7 @@ class CreateRequestPageState extends State { _userProvider = Provider.of(context); _settingProvider = Provider.of(context); _subtitle = AppLocalization.of(context).subtitle; - if (_serviceRequestsProvider == null) { - _serviceRequestsProvider = Provider.of(context); - if (widget.serviceRequest?.id != null) { - _isLoading = true; - getServiceRequestById(widget.serviceRequest.id); - } - } + _serviceRequestsProvider = Provider.of(context); return Scaffold( key: _scaffoldKey, diff --git a/lib/views/pages/user/requests/request_details.dart b/lib/views/pages/user/requests/request_details.dart index a794e5f5..de049dca 100644 --- a/lib/views/pages/user/requests/request_details.dart +++ b/lib/views/pages/user/requests/request_details.dart @@ -6,6 +6,7 @@ import 'package:test_sa/controllers/providers/api/service_requests_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/int_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/service_request/service_request.dart'; import 'package:test_sa/models/subtitle.dart'; @@ -16,7 +17,9 @@ import 'package:test_sa/views/pages/user/requests/report/create_service_report.d import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; import 'package:test_sa/views/widgets/buttons/app_button.dart'; import 'package:test_sa/views/widgets/buttons/app_icon_button.dart'; +import 'package:test_sa/views/widgets/images/files_list.dart'; import 'package:test_sa/views/widgets/images/images_list.dart'; +import 'package:test_sa/views/widgets/loaders/app_loading.dart'; import 'package:test_sa/views/widgets/loaders/image_loader.dart'; import 'package:test_sa/views/widgets/requests/info_row.dart'; import 'package:test_sa/views/widgets/requests/request_status.dart'; @@ -28,9 +31,9 @@ import 'report/future_service_report.dart'; class RequestDetailsPage extends StatelessWidget { static const String id = "/call-details"; - final ServiceRequest serviceRequest; + ServiceRequest serviceRequest; - const RequestDetailsPage({Key key, this.serviceRequest}) : super(key: key); + RequestDetailsPage({Key key, this.serviceRequest}) : super(key: key); @override Widget build(BuildContext context) { @@ -39,404 +42,423 @@ class RequestDetailsPage extends StatelessWidget { SettingProvider _settingProvider = Provider.of(context); List workOrders = []; ServiceRequestsProvider _serviceRequestsProvider = Provider.of(context); + _serviceRequestsProvider.getServiceRequestObjectById(requestId: serviceRequest.id); + return DefaultTabController( length: 2, child: Scaffold( body: SafeArea( - child: Column( - children: [ - Container( - color: AColors.primaryColor, - padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4), - child: Row( - children: [ - ABackButton(), - Expanded( - child: Center( - child: Text( - _subtitle.details, - style: Theme.of(context).textTheme.headline6.copyWith(color: AColors.white, fontStyle: FontStyle.italic), - ), - ), - ), - _userProvider.user.type == UsersTypes.normal_user - ? - // AIconButton( - // iconData: Icons.warning_amber_rounded, - // color: AColors.white, - // buttonSize: 42, - // backgroundColor: AColors.deepOrange, - // onPressed: (){ - // Navigator.of(context).push( - // MaterialPageRoute( - // builder: (_) => ReportIssuesPage(serviceRequest: serviceRequest,) - // ) - // ); - // }, - // ) - const SizedBox( - width: 48, - ) - : AIconButton( - iconData: Icons.edit, - color: AColors.white, - buttonSize: 42, - backgroundColor: AColors.green, - onPressed: () async { - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => CreateRequestPage(serviceRequest: serviceRequest)), - ); - // showModalBottomSheet( - // context: context, - // builder: (context) { - // return ServiceRequestsUpdateDialog( - // request: serviceRequest, - // ); - // }); - // DateTime picked = await showDatePicker( - // context: context, - // initialDate: DateTime.now(), - // firstDate: DateTime.now(), - // lastDate: DateTime.now().add(Duration(days: 182)) - // ); - // if(picked == null){return;} - // showDialog( - // context: context, - // barrierDismissible: false, - // builder: (BuildContext context) { - // return CupertinoAlertDialog( - // title: Text(_subtitle.updatingDots), - // content: Center(child: CircularProgressIndicator()), - // ); - // }, - // ); - // int status = await _serviceRequestsProvider.updateDate( - // user: _userProvider.user, - // host: _settingProvider.host, - // request: serviceRequest, - // newDate: picked.toString().split(" ").first - // ); - // Navigator.of(context).pop(); - // Fluttertoast.showToast( - // msg: HttpStatusManger.getStatusMessage(status: status, subtitle: _subtitle), - // ); - }, - ), - SizedBox( - width: 16, - ) - ], - ), - ), - serviceRequest.devicePhotos.isEmpty - ? SizedBox.shrink() - : Column( - children: [ - SizedBox( - height: 8, - ), - MaterialButton( - padding: EdgeInsets.zero, - onPressed: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (_) => Scaffold( - body: InteractiveViewer( - child: Center( - child: ImageLoader( - url: serviceRequest.devicePhotos.first, - boxFit: BoxFit.contain, - ), - ), - ), - ))); - }, - child: SizedBox( - height: 140 * AppStyle.getScaleFactor(context), - width: MediaQuery.of(context).size.width, - child: ImageLoader( - url: serviceRequest.devicePhotos.first, - boxFit: BoxFit.cover, - ), - ), - ), - SizedBox(height: 8), - SizedBox( - height: 60 * AppStyle.getScaleFactor(context), - child: ImagesList( - images: serviceRequest.devicePhotos, - ), - ), - ], - ), - TabBar(labelColor: AColors.primaryColor, tabs: [ - Tab( - text: _subtitle.general, - ), - Tab( - text: _subtitle.serviceRequestInformation, - ), - ]), - SizedBox( - height: 8, - ), - Expanded( - child: TabBarView( - children: [ - ListView( - padding: EdgeInsets.symmetric(horizontal: 16), - children: [ - RequestInfoRow( - title: _subtitle.callId, - info: serviceRequest.requestCode, - ), - RequestInfoRow( - title: "Asset Number", - info: serviceRequest.deviceNumber, - ), - RequestInfoRow( - title: _subtitle.assetName, - info: serviceRequest.deviceEnName, - ), - RequestInfoRow( - title: _subtitle.deviceModel, - info: serviceRequest.deviceModel, - ), - RequestInfoRow( - title: _subtitle.engineerName, - info: serviceRequest.engineerName, - ), - RequestInfoRow( - title: _subtitle.engineerPhone, - info: serviceRequest.engineerMobile, - ), - RequestInfoRow( - title: _subtitle.date, - info: serviceRequest.date, - ), - serviceRequest.nextVisitDate == null - ? SizedBox.shrink() - : RequestInfoRow( - title: _subtitle.nextVisitDate, - info: DateFormat('EE dd/MM/yyyy').format(serviceRequest.nextVisitDate), - ), - Row( + child: FutureBuilder( + future: _serviceRequestsProvider.getServiceRequestObjectById(requestId: serviceRequest.id), + builder: (context, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const ALoading(); + } else if (snap.hasData) { + serviceRequest = snap.data; + return Column( + children: [ + Container( + color: AColors.primaryColor, + padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4), + child: Row( children: [ + ABackButton(), Expanded( - child: Text( - "${_subtitle.status} : ", - style: Theme.of(context).textTheme.subtitle1, - textScaleFactor: AppStyle.getScaleFactor(context), + child: Center( + child: Text( + _subtitle.details, + style: Theme.of(context).textTheme.headline6.copyWith(color: AColors.white, fontStyle: FontStyle.italic), + ), ), ), - StatusLabel(label: serviceRequest.statusLabel, color: AColors.getRequestStatusColor(serviceRequest.statusValue)), + _userProvider.user.type == UsersTypes.normal_user + ? + // AIconButton( + // iconData: Icons.warning_amber_rounded, + // color: AColors.white, + // buttonSize: 42, + // backgroundColor: AColors.deepOrange, + // onPressed: (){ + // Navigator.of(context).push( + // MaterialPageRoute( + // builder: (_) => ReportIssuesPage(serviceRequest: serviceRequest,) + // ) + // ); + // }, + // ) + const SizedBox( + width: 48, + ) + : AIconButton( + iconData: Icons.edit, + color: AColors.white, + buttonSize: 42, + backgroundColor: AColors.green, + onPressed: () async { + Navigator.of(context).push( + MaterialPageRoute(builder: (_) => CreateRequestPage(serviceRequest: serviceRequest)), + ); + // showModalBottomSheet( + // context: context, + // builder: (context) { + // return ServiceRequestsUpdateDialog( + // request: serviceRequest, + // ); + // }); + // DateTime picked = await showDatePicker( + // context: context, + // initialDate: DateTime.now(), + // firstDate: DateTime.now(), + // lastDate: DateTime.now().add(Duration(days: 182)) + // ); + // if(picked == null){return;} + // showDialog( + // context: context, + // barrierDismissible: false, + // builder: (BuildContext context) { + // return CupertinoAlertDialog( + // title: Text(_subtitle.updatingDots), + // content: Center(child: CircularProgressIndicator()), + // ); + // }, + // ); + // int status = await _serviceRequestsProvider.updateDate( + // user: _userProvider.user, + // host: _settingProvider.host, + // request: serviceRequest, + // newDate: picked.toString().split(" ").first + // ); + // Navigator.of(context).pop(); + // Fluttertoast.showToast( + // msg: HttpStatusManger.getStatusMessage(status: status, subtitle: _subtitle), + // ); + }, + ), + SizedBox( + width: 16, + ) ], ), - Divider( - color: Theme.of(context).primaryColor, - ), - RequestInfoRow( - title: _subtitle.hospital, - info: serviceRequest.hospitalName, - ), - RequestInfoRow( - title: _subtitle.unite, - info: serviceRequest.departmentName, + ), + serviceRequest.devicePhotos.isEmpty + ? const SizedBox.shrink() + : SizedBox( + height: 60 * AppStyle.getScaleFactor(context), + child: FilesList( + images: serviceRequest.devicePhotos, + ), + ).paddingOnly(top: 4, bottom: 4), + + // Column( + // children: [ + // SizedBox( + // height: 8, + // ), + // MaterialButton( + // padding: EdgeInsets.zero, + // onPressed: () { + // Navigator.of(context).push(MaterialPageRoute( + // builder: (_) => Scaffold( + // body: InteractiveViewer( + // child: Center( + // child: ImageLoader( + // url: serviceRequest.devicePhotos.first, + // boxFit: BoxFit.contain, + // ), + // ), + // ), + // ))); + // }, + // child: SizedBox( + // height: 140 * AppStyle.getScaleFactor(context), + // width: MediaQuery.of(context).size.width, + // child: ImageLoader( + // url: serviceRequest.devicePhotos.first, + // boxFit: BoxFit.cover, + // ), + // ), + // ), + // SizedBox(height: 8), + // SizedBox( + // height: 60 * AppStyle.getScaleFactor(context), + // child: FilesList( + // images: serviceRequest.devicePhotos, + // ), + // ), + // ], + // ), + TabBar(labelColor: AColors.primaryColor, tabs: [ + Tab( + text: _subtitle.general, ), - // RequestInfoRow( - // title: _subtitle.deviceArName, - // content: serviceRequest.deviceArName, - // ), - // RequestInfoRow( - // title: _subtitle.deviceName, - // content: serviceRequest.deviceEnName, - // ), - RequestInfoRow( - title: _subtitle.maintenanceIssue, - content: serviceRequest.callComments, + Tab( + text: _subtitle.serviceRequestInformation, ), - if (serviceRequest.audio?.isNotEmpty == true) - ASoundPlayer( - audio: serviceRequest.audio, - ), - // - // Center( - // child: Padding( - // padding: EdgeInsets.all(32), - // child: AButton( - // text: _subtitle.duplicateRequest, - // onPressed: () async { - // bool result = await showDialog( - // context: context, - // builder: (_) => AAlertDialog( - // title: _subtitle.duplicateAlert, - // content: _subtitle.duplicateAlertMessage, - // ) - // ); - // if(result == true){ - // showDialog( - // context: context, - // builder: (context){ - // return Center(child: CircularProgressIndicator()); - // } - // ); - // int status = await _serviceRequestsProvider.createDuplicatedReport( - // host: _settingProvider.host, - // user: _userProvider.user, - // request: serviceRequest - // ); - // Navigator.of(context).pop(); - // ScaffoldMessenger.of(context).showSnackBar( - // SnackBar( - // content: Text( - // HttpStatusManger.getStatusMessage(status: status, subtitle: _subtitle) - // ) - // ) - // ); - // } - // }, - // ), - // ), - // ) - ], - ), - // workOrders.isEmpty ? - FutureBuilder( - future: _serviceRequestsProvider.searchWorkOrders(callId: serviceRequest.requestCode), - builder: (context, snap) { - workOrders = snap.data as List; - if (snap.connectionState == ConnectionState.waiting) return Center(child: CircularProgressIndicator()); - if (snap.connectionState == ConnectionState.done && (snap.data?.length ?? 0) != 0) { - return SingleChildScrollView( - child: Column( + ]), + SizedBox( + height: 8, + ), + Expanded( + child: TabBarView( + children: [ + ListView( + padding: EdgeInsets.symmetric(horizontal: 16), children: [ - ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - padding: EdgeInsets.all(21), - itemCount: workOrders.length, - separatorBuilder: (czt, index) => 21.height, - itemBuilder: (context, index) { - Color itemColor = index % 2 == 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onPrimary; - Color onItemColor = index % 2 != 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onPrimary; - - return ElevatedButton( - style: ElevatedButton.styleFrom( - padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8), - primary: itemColor.withOpacity(.7), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppStyle.getBorderRadius(context)), - ), - ), - //padding: EdgeInsets.symmetric(vertical: 8,horizontal: 8), - onPressed: () { - // onPressed(request); - }, - child: Column( - children: [ - RequestInfoRow( - title: _subtitle.callId, - content: serviceRequest.requestCode, - ), - RequestInfoRow( - title: _subtitle.orderWorkNumber, - info: workOrders[index].workOrderNo.toString(), - ), - RequestInfoRow( - title: _subtitle.visitDate, - info: workOrders[index].visitDate, - ), - RequestInfoRow(title: _subtitle.assignedEmployee, info: workOrders[index].assignedEmployee?.name ?? ""), - RequestInfoRow( - title: _subtitle.assetSN, - info: workOrders[index].callRequest.asset.assetSerialNo, - ), - RequestInfoRow( - title: _subtitle.assetName, - info: workOrders[index].callRequest.asset.modelDefinition.assetName, - ), - RequestInfoRow( - title: _subtitle.assetNumber, - info: workOrders[index].callRequest.asset.assetNumber, - ), - RequestInfoRow( - title: _subtitle.model, - info: workOrders[index].callRequest.asset.modelDefinition.modelName, - ), - RequestInfoRow( - title: _subtitle.site, - info: workOrders[index].callRequest.asset.site.custName, - ), - RequestInfoRow( - title: "Call last Situation", - info: workOrders[index].currentSituation.name ?? '', - ), - _userProvider.user.type == UsersTypes.engineer && workOrders[index].workOrderNo != null - ? Padding( - padding: EdgeInsets.all(32), - child: AButton( - text: _subtitle.editServiceReport, - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => FutureServiceReport( - request: serviceRequest, - workOrder: workOrders[index], - )), - ); - }, - ), - ) - : SizedBox.shrink(), - ], - ), - ); - }, + RequestInfoRow( + title: _subtitle.callId, + info: serviceRequest.requestCode, + ), + RequestInfoRow( + title: "Asset Number", + info: serviceRequest.deviceNumber, + ), + RequestInfoRow( + title: _subtitle.assetName, + info: serviceRequest.deviceEnName, + ), + RequestInfoRow( + title: _subtitle.deviceModel, + info: serviceRequest.deviceModel, ), - if (_userProvider.user.type == UsersTypes.engineer) - Center( - child: Padding( - padding: const EdgeInsets.all(32), - child: AButton( - text: "Create Work Order", - onPressed: () { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => CreateServiceReport(request: serviceRequest))); - }, + RequestInfoRow( + title: _subtitle.engineerName, + info: serviceRequest.engineerName, + ), + RequestInfoRow( + title: _subtitle.engineerPhone, + info: serviceRequest.engineerMobile, + ), + RequestInfoRow( + title: _subtitle.date, + info: serviceRequest.date, + ), + serviceRequest.nextVisitDate == null + ? SizedBox.shrink() + : RequestInfoRow( + title: _subtitle.nextVisitDate, + info: DateFormat('EE dd/MM/yyyy').format(serviceRequest.nextVisitDate), + ), + Row( + children: [ + Expanded( + child: Text( + "${_subtitle.status} : ", + style: Theme.of(context).textTheme.subtitle1, + textScaleFactor: AppStyle.getScaleFactor(context), ), ), + StatusLabel(label: serviceRequest.statusLabel, color: AColors.getRequestStatusColor(serviceRequest.statusValue)), + ], + ), + Divider( + color: Theme.of(context).primaryColor, + ), + RequestInfoRow( + title: _subtitle.hospital, + info: serviceRequest.hospitalName, + ), + RequestInfoRow( + title: _subtitle.unite, + info: serviceRequest.departmentName, + ), + // RequestInfoRow( + // title: _subtitle.deviceArName, + // content: serviceRequest.deviceArName, + // ), + // RequestInfoRow( + // title: _subtitle.deviceName, + // content: serviceRequest.deviceEnName, + // ), + RequestInfoRow( + title: _subtitle.maintenanceIssue, + content: serviceRequest.callComments, + ), + if (serviceRequest.audio?.isNotEmpty == true) + ASoundPlayer( + audio: serviceRequest.audio, ), + // + // Center( + // child: Padding( + // padding: EdgeInsets.all(32), + // child: AButton( + // text: _subtitle.duplicateRequest, + // onPressed: () async { + // bool result = await showDialog( + // context: context, + // builder: (_) => AAlertDialog( + // title: _subtitle.duplicateAlert, + // content: _subtitle.duplicateAlertMessage, + // ) + // ); + // if(result == true){ + // showDialog( + // context: context, + // builder: (context){ + // return Center(child: CircularProgressIndicator()); + // } + // ); + // int status = await _serviceRequestsProvider.createDuplicatedReport( + // host: _settingProvider.host, + // user: _userProvider.user, + // request: serviceRequest + // ); + // Navigator.of(context).pop(); + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar( + // content: Text( + // HttpStatusManger.getStatusMessage(status: status, subtitle: _subtitle) + // ) + // ) + // ); + // } + // }, + // ), + // ), + // ) ], ), - ); - } else { - return Column( - children: [ - Expanded( - child: Center( - child: ASubTitle(_subtitle.dataNotFound), - ), - ), - if (_userProvider.user.type == UsersTypes.engineer) - Center( - child: Padding( - padding: const EdgeInsets.all(32), - child: AButton( - text: "Create Work Order", - onPressed: () { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => CreateServiceReport(request: serviceRequest))); - }, + // workOrders.isEmpty ? + FutureBuilder( + future: _serviceRequestsProvider.searchWorkOrders(callId: serviceRequest.requestCode), + builder: (context, snap) { + workOrders = snap.data as List; + if (snap.connectionState == ConnectionState.waiting) return Center(child: CircularProgressIndicator()); + if (snap.connectionState == ConnectionState.done && (snap.data?.length ?? 0) != 0) { + return SingleChildScrollView( + child: Column( + children: [ + ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsets.all(21), + itemCount: workOrders.length, + separatorBuilder: (czt, index) => 21.height, + itemBuilder: (context, index) { + Color itemColor = index % 2 == 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onPrimary; + Color onItemColor = index % 2 != 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onPrimary; + + return ElevatedButton( + style: ElevatedButton.styleFrom( + padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8), + primary: itemColor.withOpacity(.7), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppStyle.getBorderRadius(context)), + ), + ), + //padding: EdgeInsets.symmetric(vertical: 8,horizontal: 8), + onPressed: () { + // onPressed(request); + }, + child: Column( + children: [ + RequestInfoRow( + title: _subtitle.callId, + content: serviceRequest.requestCode, + ), + RequestInfoRow( + title: _subtitle.orderWorkNumber, + info: workOrders[index].workOrderNo.toString(), + ), + RequestInfoRow( + title: _subtitle.visitDate, + info: workOrders[index].visitDate, + ), + RequestInfoRow(title: _subtitle.assignedEmployee, info: workOrders[index].assignedEmployee?.name ?? ""), + RequestInfoRow( + title: _subtitle.assetSN, + info: workOrders[index].callRequest.asset.assetSerialNo, + ), + RequestInfoRow( + title: _subtitle.assetName, + info: workOrders[index].callRequest.asset.modelDefinition.assetName, + ), + RequestInfoRow( + title: _subtitle.assetNumber, + info: workOrders[index].callRequest.asset.assetNumber, + ), + RequestInfoRow( + title: _subtitle.model, + info: workOrders[index].callRequest.asset.modelDefinition.modelName, + ), + RequestInfoRow( + title: _subtitle.site, + info: workOrders[index].callRequest.asset.site.custName, + ), + RequestInfoRow( + title: "Call last Situation", + info: workOrders[index].currentSituation.name ?? '', + ), + _userProvider.user.type == UsersTypes.engineer && workOrders[index].workOrderNo != null + ? Padding( + padding: EdgeInsets.all(32), + child: AButton( + text: _subtitle.editServiceReport, + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => FutureServiceReport( + request: serviceRequest, + workOrder: workOrders[index], + )), + ); + }, + ), + ) + : SizedBox.shrink(), + ], + ), + ); + }, + ), + if (_userProvider.user.type == UsersTypes.engineer) + Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: AButton( + text: "Create Work Order", + onPressed: () { + Navigator.of(context).push(MaterialPageRoute(builder: (_) => CreateServiceReport(request: serviceRequest))); + }, + ), + ), + ), + ], ), - ), - ), - ], - ); - } - }, - ), - ], - ), - ), - ], - ), + ); + } else { + return Column( + children: [ + Expanded( + child: Center( + child: ASubTitle(_subtitle.dataNotFound), + ), + ), + if (_userProvider.user.type == UsersTypes.engineer) + Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: AButton( + text: "Create Work Order", + onPressed: () { + Navigator.of(context).push(MaterialPageRoute(builder: (_) => CreateServiceReport(request: serviceRequest))); + }, + ), + ), + ), + ], + ); + } + }, + ), + ], + ), + ), + ], + ); + } + return const SizedBox(); + }), ), ), ); diff --git a/lib/views/widgets/images/files_list.dart b/lib/views/widgets/images/files_list.dart new file mode 100644 index 00000000..70bf5305 --- /dev/null +++ b/lib/views/widgets/images/files_list.dart @@ -0,0 +1,73 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:open_file/open_file.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/views/app_style/sizing.dart'; +import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; +import 'package:test_sa/views/widgets/loaders/image_loader.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import 'image_item.dart'; +import 'images_viewer.dart'; + +class FilesList extends StatelessWidget { + final List images; + final EdgeInsets padding; + + const FilesList({Key key, this.images, this.padding}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ListView.builder( + padding: padding ?? EdgeInsets.symmetric(horizontal: 32 * AppStyle.getScaleFactor(context)), + scrollDirection: Axis.horizontal, + itemCount: images.length, + itemBuilder: (context, itemIndex) { + var isImage = + images[itemIndex].split(".").last.toLowerCase() == "png" || images[itemIndex].split(".").last.toLowerCase() == "jpg" || images[itemIndex].split(".").last.toLowerCase() == "jpeg"; + var isPdf = images[itemIndex].split(".").last.toLowerCase() == "pdf"; + var isExcel = images[itemIndex].split(".").last.toLowerCase() == "xlsx"; + + return (isImage + ? (_isLocalUrl(images[itemIndex]) + ? Image.file(File(images[itemIndex])) + : ImageLoader( + url: images[itemIndex], + boxFit: BoxFit.cover, + )) + : Image.asset("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png")) + .onPress(() async { + if (isImage) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => Scaffold( + body: SafeArea( + child: Stack( + children: [ + InteractiveViewer(child: Image(image: (_isLocalUrl(images[itemIndex]) ? FileImage(File(images[itemIndex])) : NetworkImage(images[itemIndex])))).center, + const ABackButton(), + ], + ), + ), + ), + ), + ); + } else if (_isLocalUrl(images[itemIndex])) { + OpenFile.open(images[itemIndex]); + } else { + if (!await launchUrl(Uri.parse(images[itemIndex]), mode: LaunchMode.externalApplication)) { + Fluttertoast.showToast(msg: "UnExpected Error with file."); + throw Exception('Could not launch '); + } + } + }); + }); + } + + bool _isLocalUrl(String url) { + if (url?.isEmpty != false) return false; + return url.startsWith("/") || url.startsWith("file://") || url.substring(1).startsWith(':\\'); + } +} diff --git a/lib/views/widgets/images/multi_image_picker_item.dart b/lib/views/widgets/images/multi_image_picker_item.dart index 5cddee73..d5b00011 100644 --- a/lib/views/widgets/images/multi_image_picker_item.dart +++ b/lib/views/widgets/images/multi_image_picker_item.dart @@ -7,6 +7,7 @@ import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/views/app_style/colors.dart'; import 'package:test_sa/views/app_style/sizing.dart'; import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; +import 'package:test_sa/views/widgets/loaders/image_loader.dart'; import 'package:url_launcher/url_launcher.dart'; class MultiFilesPickerItem extends StatelessWidget { @@ -43,7 +44,14 @@ class MultiFilesPickerItem extends StatelessWidget { body: SafeArea( child: Stack( children: [ - InteractiveViewer(child: Image(image: (_isLocalUrl(file.path) ? FileImage(file) : NetworkImage(file.path)))).center, + InteractiveViewer( + child: _isLocalUrl(file.path) + ? Image.file(file) + : ImageLoader( + url: file.path, + boxFit: BoxFit.cover, + )) + .center, const ABackButton(), ], ), From bf30e1d763036e67a1d0925713a3e12f1c6aa064 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 31 Aug 2023 15:12:11 +0300 Subject: [PATCH 09/16] update service request attachment fixed. --- lib/views/pages/user/requests/create_request.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/views/pages/user/requests/create_request.dart b/lib/views/pages/user/requests/create_request.dart index db5b9fda..76f6dc67 100644 --- a/lib/views/pages/user/requests/create_request.dart +++ b/lib/views/pages/user/requests/create_request.dart @@ -397,7 +397,7 @@ class CreateRequestPageState extends State { _serviceRequest.requestedThrough = Provider.of(context, listen: false).getDefaultItem(); _isLoading = true; setState(() {}); - _serviceRequest.devicePhotos = _deviceImages.map((e) => "${e.path.split("/").last}|${base64Encode(e.readAsBytesSync())}").toList(); + _serviceRequest.devicePhotos = _deviceImages.map((e) => _isLocalUrl(e.path) ? "${e.path.split("/").last}|${base64Encode(e.readAsBytesSync())}" : e.path).toList(); if (_serviceRequest.audio != null) { if (_isLocalUrl(_serviceRequest.audio)) { final File file = File(_serviceRequest.audio); From 830bf6e5e6466c685380a604e9c657cb4a5424b5 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 31 Aug 2023 15:34:55 +0300 Subject: [PATCH 10/16] first action improvement. --- lib/views/pages/user/requests/create_request.dart | 7 ++++++- lib/views/widgets/date_and_time/date_picker.dart | 13 ++++++++----- lib/views/widgets/images/files_list.dart | 4 +++- .../service_request_first_action.dart | 4 +++- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/views/pages/user/requests/create_request.dart b/lib/views/pages/user/requests/create_request.dart index 76f6dc67..67633a3e 100644 --- a/lib/views/pages/user/requests/create_request.dart +++ b/lib/views/pages/user/requests/create_request.dart @@ -65,6 +65,8 @@ class CreateRequestPageState extends State { TextEditingController _maintenanceController, _commentController, _reviewCommentController; DateTime _dateTime; + bool isFirstActionSubmitted = false; + @override void initState() { _maintenanceController = TextEditingController(); @@ -80,9 +82,10 @@ class CreateRequestPageState extends State { } else { _serviceRequest = ServiceRequest(); } - + isFirstActionSubmitted = _serviceRequest?.firstAction != null; super.initState(); } + // // getServiceRequestById(String id) async { // try { @@ -274,6 +277,7 @@ class CreateRequestPageState extends State { if (widget.serviceRequest != null) ServiceRequestedFirstAction( initialValue: _serviceRequest.firstAction, + enabled: !isFirstActionSubmitted, onSelect: (status) { _dateTime = null; _serviceRequest.firstAction = status; @@ -285,6 +289,7 @@ class CreateRequestPageState extends State { if (_showDatePicker) ADatePicker( date: _dateTime, + enable: !isFirstActionSubmitted, from: DateTime.now(), onDatePicker: (date) { _dateTime = date; diff --git a/lib/views/widgets/date_and_time/date_picker.dart b/lib/views/widgets/date_and_time/date_picker.dart index 54c4ef8c..09f66465 100644 --- a/lib/views/widgets/date_and_time/date_picker.dart +++ b/lib/views/widgets/date_and_time/date_picker.dart @@ -6,8 +6,9 @@ class ADatePicker extends StatelessWidget { final DateTime from; final DateTime to; final Function(DateTime) onDatePicker; + final bool enable; - const ADatePicker({Key key, this.date, this.onDatePicker, this.from, this.to}) : super(key: key); + const ADatePicker({Key key, this.date, this.onDatePicker, this.from, this.to, this.enable = true}) : super(key: key); @override Widget build(BuildContext context) { @@ -19,14 +20,16 @@ class ADatePicker extends StatelessWidget { borderRadius: BorderRadius.circular(12 * AppStyle.getScaleFactor(context)), ), ), + onPressed: enable + ? () async { + DateTime picked = await showDatePicker(context: context, initialDate: DateTime.now(), firstDate: from ?? DateTime.now(), lastDate: to ?? DateTime.now().add(Duration(days: 365))); + onDatePicker(picked); + } + : null, child: Text( date == null ? "Pick Date" : date.toString().split(" ").first, textScaleFactor: AppStyle.getScaleFactor(context), ), - onPressed: () async { - DateTime picked = await showDatePicker(context: context, initialDate: DateTime.now(), firstDate: from ?? DateTime.now(), lastDate: to ?? DateTime.now().add(Duration(days: 365))); - onDatePicker(picked); - }, ); } } diff --git a/lib/views/widgets/images/files_list.dart b/lib/views/widgets/images/files_list.dart index 70bf5305..6cfd8a85 100644 --- a/lib/views/widgets/images/files_list.dart +++ b/lib/views/widgets/images/files_list.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:open_file/open_file.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/views/app_style/sizing.dart'; import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; @@ -20,10 +21,11 @@ class FilesList extends StatelessWidget { @override Widget build(BuildContext context) { - return ListView.builder( + return ListView.separated( padding: padding ?? EdgeInsets.symmetric(horizontal: 32 * AppStyle.getScaleFactor(context)), scrollDirection: Axis.horizontal, itemCount: images.length, + separatorBuilder: (context, itemIndex) => 8.width, itemBuilder: (context, itemIndex) { var isImage = images[itemIndex].split(".").last.toLowerCase() == "png" || images[itemIndex].split(".").last.toLowerCase() == "jpg" || images[itemIndex].split(".").last.toLowerCase() == "jpeg"; diff --git a/lib/views/widgets/status/service_request/service_request_first_action.dart b/lib/views/widgets/status/service_request/service_request_first_action.dart index 02c5f94e..99511972 100644 --- a/lib/views/widgets/status/service_request/service_request_first_action.dart +++ b/lib/views/widgets/status/service_request/service_request_first_action.dart @@ -11,8 +11,9 @@ import '../../../../controllers/providers/api/status_drop_down/service_reqest/se class ServiceRequestedFirstAction extends StatelessWidget { final Function(Lookup) onSelect; final Lookup initialValue; + final bool enabled; - const ServiceRequestedFirstAction({Key key, this.onSelect, this.initialValue}) : super(key: key); + const ServiceRequestedFirstAction({Key key, this.onSelect, this.initialValue, this.enabled = true}) : super(key: key); @override Widget build(BuildContext context) { final settingProvider = Provider.of(context); @@ -30,6 +31,7 @@ class ServiceRequestedFirstAction extends StatelessWidget { initialStatus: initialValue, statuses: menuProvider.items, onSelect: onSelect, + enabled: enabled, ), ); } From fe9ca387ed1ade8c328ff74dcda0cd5dcbc5cca0 Mon Sep 17 00:00:00 2001 From: zaid_daoud Date: Thu, 31 Aug 2023 15:50:36 +0300 Subject: [PATCH 11/16] Multiple Images Everywhere --- lib/models/pantry/pentry.dart | 14 +++--- lib/models/service_report.dart | 7 ++- .../create_sub_workorder_page.dart | 27 +++++++----- .../report/create_service_report.dart | 43 +++++++++++-------- .../requests/report/edit_service_report.dart | 3 +- .../pages/user/visits/visit_details.dart | 40 +++++++++-------- .../widgets/pentry/pentry_info_form.dart | 25 ++++++----- 7 files changed, 88 insertions(+), 71 deletions(-) diff --git a/lib/models/pantry/pentry.dart b/lib/models/pantry/pentry.dart index e1bb74fd..f2b5dfee 100644 --- a/lib/models/pantry/pentry.dart +++ b/lib/models/pantry/pentry.dart @@ -18,7 +18,7 @@ class Pentry { DateTime expectedVisitDate; String travelingHours; String image; - File file; + List files = []; // List contacts; List ppmCheckLists; @@ -37,7 +37,7 @@ class Pentry { this.actualVisitDate, this.expectedVisitDate, this.image, - this.file, + this.files, // this.contacts, this.ppmCheckLists, this.calibrationTools, @@ -86,10 +86,8 @@ class Pentry { map["endDate"] = timer.endAt?.toIso8601String() ?? DateTime.now().toIso8601String(); map["workingHours"] = (timer.durationInSecond / 60 / 60).toStringAsFixed(5); } - if (file != null) { - map["vAttachments"] = [ - {"attachmentName": (file.path.split("/").last + base64Encode(file.readAsBytesSync()))} - ]; + if (files?.isNotEmpty ?? false) { + map["vAttachments"] = files.map((file) => {"attachmentName": (file.path.split("/").last + base64Encode(file.readAsBytesSync()))}).toList(); } map["travelingHours"] = travelingHours; // if(contacts?.isNotEmpty == true) { @@ -157,7 +155,7 @@ class Pentry { DateTime expectedVisitDate, String travelingHours, String image, - File imageFile, + List files, List ppmCheckLists, List calibrationTools, List pmKits, @@ -172,7 +170,7 @@ class Pentry { expectedVisitDate: expectedVisitDate ?? this.expectedVisitDate, travelingHours: travelingHours ?? this.travelingHours, image: image ?? this.image, - file: imageFile ?? this.file, + files: files ?? this.files, ppmCheckLists: ppmCheckLists ?? this.ppmCheckLists?.map((e) => e.copyWith())?.toList(), calibrationTools: calibrationTools ?? this.calibrationTools?.map((e) => e.copyWith())?.toList(), pmKits: pmKits ?? this.pmKits.map((e) => e.copyWith()).toList(), diff --git a/lib/models/service_report.dart b/lib/models/service_report.dart index 020ef0a5..2f3da26c 100644 --- a/lib/models/service_report.dart +++ b/lib/models/service_report.dart @@ -198,6 +198,11 @@ class ServiceReport { _parts = partsList.map((e) => Part.fromJson(e["sparePart"], reportJson: e)).toList(); } } + List _files = []; + if (parsedJson["attachmentsWorkOrder"] != null && parsedJson["attachmentsWorkOrder"] is List) { + List list = parsedJson["attachmentsWorkOrder"]; + _files = list.map((e) => (URLs.getFileUrl(e["name"]))).toList(); + } return ServiceReport( id: id, assetType: Lookup.fromJson(parsedJson["assetType"]), @@ -208,7 +213,7 @@ class ServiceReport { equipmentStatus: Lookup.fromJson(parsedJson["equipmentStatus"]), type: Lookup.fromJson(parsedJson["typeOfWO"]), faultDescription: parsedJson['faultDescription'] != null ? FaultDescription.fromJson(parsedJson['faultDescription']) : null, - + files: _files, // faultDescription: parsedJson["faultDescription"], // startDate: DateTime.tryParse(parsedJson["startofWorkTime"] ?? ""), // endDate: DateTime.tryParse(parsedJson["endofWorkTime"] ?? ""), diff --git a/lib/views/pages/sub_workorder/create_sub_workorder_page.dart b/lib/views/pages/sub_workorder/create_sub_workorder_page.dart index 129790b7..056707af 100644 --- a/lib/views/pages/sub_workorder/create_sub_workorder_page.dart +++ b/lib/views/pages/sub_workorder/create_sub_workorder_page.dart @@ -16,6 +16,7 @@ import 'package:test_sa/views/app_style/colors.dart'; import 'package:test_sa/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart'; import 'package:test_sa/views/pages/sub_workorder/work_order_details_bottom_sheet.dart'; import 'package:test_sa/views/pages/sub_workorder/workorder_details.dart'; +import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; import 'package:test_sa/views/widgets/loaders/app_loading.dart'; import 'package:test_sa/views/widgets/status/service_request/service_request_defect_types_mune.dart'; @@ -27,7 +28,6 @@ import '../../widgets/app_text_form_field.dart'; import '../../widgets/buttons/app_back_button.dart'; import '../../widgets/buttons/app_button.dart'; import '../../widgets/date_and_time/date_picker.dart'; -import '../../widgets/images/mini_one_file_picker.dart'; import '../../widgets/status/report/service_report_fault_description.dart'; import '../../widgets/status/report/service_report_reasons.dart'; import '../../widgets/titles/app_sub_title.dart'; @@ -47,7 +47,7 @@ class _CreateSubWorkOrderPageState extends State { SearchWorkOrder _subWorkOrders; Lookup _serviceReportReason = const Lookup(); ServiceReport _serviceReport; - File _file; + final List _files = []; bool _isLoading = false; bool _validate = false; @@ -249,6 +249,8 @@ class _CreateSubWorkOrderPageState extends State { ], ), const SizedBox(height: 8), + MultiFilesPicker(label: subtitle.deviceFiles, files: _files), + const SizedBox(height: 8), InkWell( onTap: () async { await showModalBottomSheet( @@ -302,15 +304,14 @@ class _CreateSubWorkOrderPageState extends State { ), ), ), - const SizedBox(height: 8), - AMiniOneFilePicker( - file: _file, - onPick: (file) { - _file = file; - _subWorkOrders.attachmentsWorkOrder ??= []; - _subWorkOrders.attachmentsWorkOrder.add(AttachmentsWorkOrder(name: "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}")); - }, - ), + // AMiniOneFilePicker( + // file: _file, + // onPick: (file) { + // _file = file; + // _subWorkOrders.attachmentsWorkOrder ??= []; + // _subWorkOrders.attachmentsWorkOrder.add(AttachmentsWorkOrder(name: "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}")); + // }, + // ), const SizedBox(height: 50), ], ), @@ -327,6 +328,10 @@ class _CreateSubWorkOrderPageState extends State { _validate = true; setState(() {}); if (validate()) { + _subWorkOrders.attachmentsWorkOrder ??= []; + for (var file in _files) { + _subWorkOrders.attachmentsWorkOrder.add(AttachmentsWorkOrder(name: "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}")); + } if (_subWorkOrders.timer?.startAt == null || _subWorkOrders.timer?.endAt == null) { Fluttertoast.showToast(msg: "Working hours required"); return; diff --git a/lib/views/pages/user/requests/report/create_service_report.dart b/lib/views/pages/user/requests/report/create_service_report.dart index 3ef3f758..bdd295e0 100644 --- a/lib/views/pages/user/requests/report/create_service_report.dart +++ b/lib/views/pages/user/requests/report/create_service_report.dart @@ -24,7 +24,6 @@ import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; import 'package:test_sa/views/widgets/buttons/app_button.dart'; import 'package:test_sa/views/widgets/e_signature/e_signature.dart'; import 'package:test_sa/views/widgets/equipment/auto_complete_devices_field.dart'; -import 'package:test_sa/views/widgets/images/mini_one_file_picker.dart'; import 'package:test_sa/views/widgets/loaders/loading_manager.dart'; import 'package:test_sa/views/widgets/parts/auto_complete_parts_field.dart'; import 'package:test_sa/views/widgets/parts/part_item.dart'; @@ -35,6 +34,7 @@ import 'package:test_sa/views/widgets/titles/app_sub_title.dart'; import '../../../../../controllers/providers/api/status_drop_down/report/service_types_provider.dart'; import '../../../../widgets/date_and_time/date_picker.dart'; +import '../../../../widgets/images/multi_image_picker.dart'; import '../../../../widgets/status/report/service_report_fault_description.dart'; import '../../../../widgets/status/report/service_report_repair_location.dart'; import '../../../../widgets/timer/app_timer.dart'; @@ -60,7 +60,7 @@ class _CreateServiceReportState extends State with TickerPr bool _showCommentField = false; Subtitle _subtitle; - File _file; + final List _files = []; final GlobalKey _formKey = GlobalKey(); final GlobalKey _scaffoldKey = GlobalKey(); final TextEditingController _faultController = TextEditingController(); @@ -628,25 +628,27 @@ class _CreateServiceReportState extends State with TickerPr ), ), const SizedBox(width: 8), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const ASubTitle("Attachment"), - AMiniOneFilePicker( - //error: _validate && _serviceReport.image == null, - file: _file, - onPick: (file) { - _file = file; - _serviceReport.files[0] = "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}"; - }, - ), - ], - ), - ), + // Expanded( + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // const ASubTitle("Attachments"), + // AMiniOneFilePicker( + // //error: _validate && _serviceReport.image == null, + // file: _file, + // onPick: (file) { + // _file = file; + // _serviceReport.files[0] = "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}"; + // }, + // ), + // ], + // ), + // ), ], ), const SizedBox(height: 8), + MultiFilesPicker(label: "Attachments", files: _files), + const SizedBox(height: 8), ASubTitle(_subtitle.repairLocation), const SizedBox(height: 4), ServiceReportRepairLocation( @@ -924,7 +926,10 @@ class _CreateServiceReportState extends State with TickerPr _isLoading = true; setState(() {}); - + _serviceReport.files ??= []; + for (var file in _files) { + _serviceReport.files.add("${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}"); + } int status = await _serviceRequestsProvider.createServiceReport( user: _userProvider.user, host: _settingProvider.host, diff --git a/lib/views/pages/user/requests/report/edit_service_report.dart b/lib/views/pages/user/requests/report/edit_service_report.dart index 6aa64bdf..dc3d6d41 100644 --- a/lib/views/pages/user/requests/report/edit_service_report.dart +++ b/lib/views/pages/user/requests/report/edit_service_report.dart @@ -61,7 +61,7 @@ class _EditServiceReportState extends State with TickerProvid bool _showCommentField = false; Subtitle _subtitle; - final List _images = []; + List _images = []; final GlobalKey _formKey = GlobalKey(); final GlobalKey _scaffoldKey = GlobalKey(); TextEditingController _faultController = TextEditingController(); @@ -74,6 +74,7 @@ class _EditServiceReportState extends State with TickerProvid @override void initState() { _serviceReport = widget.report; + _images = widget.report?.files?.map((e) => File(e))?.toList(); super.initState(); } diff --git a/lib/views/pages/user/visits/visit_details.dart b/lib/views/pages/user/visits/visit_details.dart index 7fb05200..ddc88782 100644 --- a/lib/views/pages/user/visits/visit_details.dart +++ b/lib/views/pages/user/visits/visit_details.dart @@ -9,12 +9,12 @@ import 'package:test_sa/views/pages/user/visits/pantry/edit_pentry.dart'; import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; import 'package:test_sa/views/widgets/buttons/app_icon_button.dart'; import 'package:test_sa/views/widgets/images/images_list.dart'; -import 'package:test_sa/views/widgets/loaders/image_loader.dart'; import 'package:test_sa/views/widgets/requests/info_row.dart'; import 'package:test_sa/views/widgets/visits/visit_status.dart'; import '../../../../controllers/providers/api/user_provider.dart'; import '../../../../models/enums/user_types.dart'; +import '../../../widgets/loaders/image_loader.dart'; class VisitDetailsPage extends StatelessWidget { static const String id = "/visit-details"; @@ -51,11 +51,14 @@ class VisitDetailsPage extends StatelessWidget { buttonSize: 42, backgroundColor: AColors.green, onPressed: () async { - Navigator.of(context).push(MaterialPageRoute( + Navigator.of(context).push( + MaterialPageRoute( builder: (_) => EditPentry( - visit: visit, - pentry: visit.pentry, - ))); + visit: visit, + pentry: visit.pentry, + ), + ), + ); }, ), const SizedBox(width: 42) @@ -66,24 +69,25 @@ class VisitDetailsPage extends StatelessWidget { child: ListView( padding: const EdgeInsets.symmetric(horizontal: 16), children: [ - const SizedBox( - height: 8, - ), + const SizedBox(height: 8), MaterialButton( padding: EdgeInsets.zero, onPressed: () { - if (visit.images.isEmpty || (visit.images?.first?.isEmpty ?? false)) { - Navigator.of(context).push(MaterialPageRoute( + if ((visit.images?.isNotEmpty ?? false) && (visit.images?.first?.isNotEmpty ?? false)) { + Navigator.of(context).push( + MaterialPageRoute( builder: (_) => Scaffold( - body: InteractiveViewer( - child: Center( - child: ImageLoader( - url: visit.images.first, - boxFit: BoxFit.contain, - ), - ), + body: InteractiveViewer( + child: Center( + child: ImageLoader( + url: visit.images.first, + boxFit: BoxFit.contain, ), - ))); + ), + ), + ), + ), + ); } }, child: SizedBox( diff --git a/lib/views/widgets/pentry/pentry_info_form.dart b/lib/views/widgets/pentry/pentry_info_form.dart index 1484a2ca..73c4364f 100644 --- a/lib/views/widgets/pentry/pentry_info_form.dart +++ b/lib/views/widgets/pentry/pentry_info_form.dart @@ -9,11 +9,11 @@ import 'package:test_sa/views/app_style/sizing.dart'; import 'package:test_sa/views/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 'package:test_sa/views/widgets/images/mini_one_file_picker.dart'; import 'package:test_sa/views/widgets/status/pentry/pentry_visit_status_mune.dart'; import 'package:test_sa/views/widgets/timer/app_timer.dart'; import 'package:test_sa/views/widgets/titles/app_sub_title.dart'; +import '../images/multi_image_picker.dart'; import '../status/pentry/pentry_status_mune.dart'; class PentryInfoForm extends StatefulWidget { @@ -33,6 +33,7 @@ class PentryInfoForm extends StatefulWidget { class _PentryInfoFormState extends State { @override Widget build(BuildContext context) { + widget.model?.files ??= []; final subtitle = AppLocalization.of(context).subtitle; final userProvider = Provider.of(context); return ListView( @@ -149,23 +150,21 @@ class _PentryInfoFormState extends State { ATextFormField( initialValue: (widget.model.travelingHours ?? "").toString(), textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subtitle1, + style: Theme.of(context).textTheme.titleMedium, textInputType: TextInputType.number, onChange: (value) { widget.model.travelingHours = value; }, ), - const SizedBox( - height: 12, - ), - const ASubTitle("PPM Attachment"), - AMiniOneFilePicker( - //error: _validate && _serviceReport.image == null, - file: widget.model.file, - onPick: (file) { - widget.model.file = file; - }, - ), + const SizedBox(height: 12), + MultiFilesPicker(label: "PPM Attachments", files: widget.model.files), + // AMiniOneFilePicker( + // //error: _validate && _serviceReport.image == null, + // file: widget.model.file, + // onPick: (file) { + // widget.model.file = file; + // }, + // ), const SizedBox( height: 8, ), From dc2dab5869e84cbdaa560af9c9a542dd0b83ebd3 Mon Sep 17 00:00:00 2001 From: zaid_daoud Date: Thu, 31 Aug 2023 15:57:56 +0300 Subject: [PATCH 12/16] Multiple Images Everywhere --- lib/models/pantry/pentry.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/models/pantry/pentry.dart b/lib/models/pantry/pentry.dart index f2b5dfee..68fd85dd 100644 --- a/lib/models/pantry/pentry.dart +++ b/lib/models/pantry/pentry.dart @@ -117,7 +117,11 @@ class Pentry { if (map['vKits'] != null) { pmKits = (map['vKits'] as List).map((e) => PMKit.fromMap(e as Map)).toList(); } - + List files = []; + if (map["vAttachments"] != null) if (map["vAttachments"] is List) { + List list = map["vAttachments"]; + files = list.map((e) => URLs.getFileUrl(e["name"])).toList(); + } List ppmCheckLists = []; if (map['vChecklists'] != null) { ppmCheckLists = (map['vChecklists'] as List).map((e) => PPMCheckList.fromMap(e as Map)).toList(); @@ -142,6 +146,7 @@ class Pentry { ppmCheckLists: ppmCheckLists, calibrationTools: calibrationTools, pmKits: pmKits, + files: files.map((e) => File(e)).toList(), signatureNurse: URLs.getFileUrl(map["nurseSignature"]), signatureEngineer: URLs.getFileUrl(map["engSignature"]), ); From a41944e78a2a7100976d807e8d1a6dcad1d75d75 Mon Sep 17 00:00:00 2001 From: zaid_daoud Date: Thu, 31 Aug 2023 16:00:46 +0300 Subject: [PATCH 13/16] Enhancements --- lib/views/pages/sub_workorder/create_sub_workorder_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/views/pages/sub_workorder/create_sub_workorder_page.dart b/lib/views/pages/sub_workorder/create_sub_workorder_page.dart index 056707af..8534f52a 100644 --- a/lib/views/pages/sub_workorder/create_sub_workorder_page.dart +++ b/lib/views/pages/sub_workorder/create_sub_workorder_page.dart @@ -249,7 +249,7 @@ class _CreateSubWorkOrderPageState extends State { ], ), const SizedBox(height: 8), - MultiFilesPicker(label: subtitle.deviceFiles, files: _files), + MultiFilesPicker(label: "Attachments", files: _files), const SizedBox(height: 8), InkWell( onTap: () async { From a9d4d2a3739e9fa2bc3403ee6907967a6bc2e51d Mon Sep 17 00:00:00 2001 From: zaid_daoud Date: Thu, 31 Aug 2023 16:36:19 +0300 Subject: [PATCH 14/16] Enhancements --- .../pages/user/requests/report/edit_service_report.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/views/pages/user/requests/report/edit_service_report.dart b/lib/views/pages/user/requests/report/edit_service_report.dart index dc3d6d41..7043ddbf 100644 --- a/lib/views/pages/user/requests/report/edit_service_report.dart +++ b/lib/views/pages/user/requests/report/edit_service_report.dart @@ -1071,7 +1071,7 @@ class _EditServiceReportState extends State with TickerProvid _isLoading = true; setState(() {}); - _serviceReport.files = _images.map((e) => base64Encode(e.readAsBytesSync())).toList(); + _serviceReport.files = _images.map((e) => _isLocalUrl(e.path) ? "${e.path.split("/").last}|${base64Encode(e.readAsBytesSync())}" : e.path).toList(); int status = await _serviceRequestsProvider.updateServiceReport(user: _userProvider.user, host: _settingProvider.host, report: _serviceReport, request: widget.request); _isLoading = false; setState(() {}); @@ -1099,4 +1099,9 @@ class _EditServiceReportState extends State with TickerProvid ), ); } + + bool _isLocalUrl(String url) { + if (url?.isEmpty != false) return false; + return url.startsWith("/") || url.startsWith("file://") || url.substring(1).startsWith(':\\'); + } } From d63a18226a5625184e0bfaf85b344b3955d70281 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 31 Aug 2023 17:50:41 +0300 Subject: [PATCH 15/16] ppm attachments fix --- lib/models/pantry/pentry.dart | 2 +- lib/models/visits/visit.dart | 5 +- .../pages/user/visits/visit_details.dart | 78 ++++++++++--------- lib/views/widgets/images/files_list.dart | 16 +++- 4 files changed, 58 insertions(+), 43 deletions(-) diff --git a/lib/models/pantry/pentry.dart b/lib/models/pantry/pentry.dart index 68fd85dd..57f871c1 100644 --- a/lib/models/pantry/pentry.dart +++ b/lib/models/pantry/pentry.dart @@ -120,7 +120,7 @@ class Pentry { List files = []; if (map["vAttachments"] != null) if (map["vAttachments"] is List) { List list = map["vAttachments"]; - files = list.map((e) => URLs.getFileUrl(e["name"])).toList(); + files = list.map((e) => URLs.getFileUrl(e["attachmentName"])).toList(); } List ppmCheckLists = []; if (map['vChecklists'] != null) { diff --git a/lib/models/visits/visit.dart b/lib/models/visits/visit.dart index 9d8ad663..994df35e 100644 --- a/lib/models/visits/visit.dart +++ b/lib/models/visits/visit.dart @@ -1,3 +1,4 @@ +import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/pantry/pentry.dart'; @@ -50,7 +51,9 @@ class Visit { List images = []; if (parsedJson["vAttachments"] is List) { List list = parsedJson["vAttachments"]; - images = list.map((e) => e["attachmentURL"].toString()).toList(); + images = list.map((e) { + return URLs.getFileUrl(e["attachmentURL"].toString()); + }).toList(); } return Visit( id: parsedJson["id"], diff --git a/lib/views/pages/user/visits/visit_details.dart b/lib/views/pages/user/visits/visit_details.dart index ddc88782..9d60fcc6 100644 --- a/lib/views/pages/user/visits/visit_details.dart +++ b/lib/views/pages/user/visits/visit_details.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/localization/localization.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/subtitle.dart'; import 'package:test_sa/models/visits/visit.dart'; import 'package:test_sa/views/app_style/colors.dart'; @@ -8,6 +9,7 @@ import 'package:test_sa/views/app_style/sizing.dart'; import 'package:test_sa/views/pages/user/visits/pantry/edit_pentry.dart'; import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; import 'package:test_sa/views/widgets/buttons/app_icon_button.dart'; +import 'package:test_sa/views/widgets/images/files_list.dart'; import 'package:test_sa/views/widgets/images/images_list.dart'; import 'package:test_sa/views/widgets/requests/info_row.dart'; import 'package:test_sa/views/widgets/visits/visit_status.dart'; @@ -21,6 +23,7 @@ class VisitDetailsPage extends StatelessWidget { final Visit visit; const VisitDetailsPage({Key key, this.visit}) : super(key: key); + @override Widget build(BuildContext context) { Subtitle subtitle = AppLocalization.of(context).subtitle; @@ -70,44 +73,45 @@ class VisitDetailsPage extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 16), children: [ const SizedBox(height: 8), - MaterialButton( - padding: EdgeInsets.zero, - onPressed: () { - if ((visit.images?.isNotEmpty ?? false) && (visit.images?.first?.isNotEmpty ?? false)) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => Scaffold( - body: InteractiveViewer( - child: Center( - child: ImageLoader( - url: visit.images.first, - boxFit: BoxFit.contain, - ), - ), - ), - ), - ), - ); - } - }, - child: SizedBox( - height: 140 * AppStyle.getScaleFactor(context), - width: MediaQuery.of(context).size.width, - child: ImageLoader( - url: visit.images.isEmpty ? " " : visit.images.first, - boxFit: BoxFit.cover, + // MaterialButton( + // padding: EdgeInsets.zero, + // onPressed: () { + // if ((visit.images?.isNotEmpty ?? false) && (visit.images?.first?.isNotEmpty ?? false)) { + // Navigator.of(context).push( + // MaterialPageRoute( + // builder: (_) => Scaffold( + // body: InteractiveViewer( + // child: Center( + // child: ImageLoader( + // url: visit.images.first, + // boxFit: BoxFit.contain, + // ), + // ), + // ), + // ), + // ), + // ); + // } + // }, + // child: SizedBox( + // height: 140 * AppStyle.getScaleFactor(context), + // width: MediaQuery.of(context).size.width, + // child: ImageLoader( + // url: visit.images.isEmpty ? " " : visit.images.first, + // boxFit: BoxFit.cover, + // ), + // ), + // ), + // const SizedBox( + // height: 8, + // ), + if (visit?.images?.isNotEmpty ?? false) + SizedBox( + height: 60 * AppStyle.getScaleFactor(context), + child: FilesList( + images: visit.images, ), - ), - ), - const SizedBox( - height: 8, - ), - SizedBox( - height: 60 * AppStyle.getScaleFactor(context), - child: ImagesList( - images: visit.images, - ), - ), + ).paddingOnly(top: 4, bottom: 4), const SizedBox( height: 8, ), diff --git a/lib/views/widgets/images/files_list.dart b/lib/views/widgets/images/files_list.dart index 6cfd8a85..d7f21dff 100644 --- a/lib/views/widgets/images/files_list.dart +++ b/lib/views/widgets/images/files_list.dart @@ -21,12 +21,16 @@ class FilesList extends StatelessWidget { @override Widget build(BuildContext context) { + return ListView.separated( padding: padding ?? EdgeInsets.symmetric(horizontal: 32 * AppStyle.getScaleFactor(context)), scrollDirection: Axis.horizontal, - itemCount: images.length, + itemCount: images?.length ?? 0, separatorBuilder: (context, itemIndex) => 8.width, itemBuilder: (context, itemIndex) { + if (images[itemIndex] == null || !images[itemIndex].contains(".")) { + return Text("Bad\nFile"); + } var isImage = images[itemIndex].split(".").last.toLowerCase() == "png" || images[itemIndex].split(".").last.toLowerCase() == "jpg" || images[itemIndex].split(".").last.toLowerCase() == "jpeg"; var isPdf = images[itemIndex].split(".").last.toLowerCase() == "pdf"; @@ -59,9 +63,13 @@ class FilesList extends StatelessWidget { } else if (_isLocalUrl(images[itemIndex])) { OpenFile.open(images[itemIndex]); } else { - if (!await launchUrl(Uri.parse(images[itemIndex]), mode: LaunchMode.externalApplication)) { - Fluttertoast.showToast(msg: "UnExpected Error with file."); - throw Exception('Could not launch '); + try { + if (!await launchUrl(Uri.parse(images[itemIndex]), mode: LaunchMode.externalApplication)) { + Fluttertoast.showToast(msg: "UnExpected Error with file."); + throw Exception('Could not launch '); + } + } catch (ex) { + Fluttertoast.showToast(msg: "No Viewer found."); } } }); From 2384bab11f6721e40085592d9496ff73d5e29926 Mon Sep 17 00:00:00 2001 From: zaid_daoud Date: Sun, 3 Sep 2023 13:31:36 +0300 Subject: [PATCH 16/16] Fault Desc Issue Fixed --- .../report/create_service_report.dart | 6 +- .../status/report/fault_desc_menu.dart | 70 ++++++++++--------- .../service_report_fault_description.dart | 25 +++---- 3 files changed, 54 insertions(+), 47 deletions(-) diff --git a/lib/views/pages/user/requests/report/create_service_report.dart b/lib/views/pages/user/requests/report/create_service_report.dart index bdd295e0..80e14e25 100644 --- a/lib/views/pages/user/requests/report/create_service_report.dart +++ b/lib/views/pages/user/requests/report/create_service_report.dart @@ -547,8 +547,10 @@ class _CreateServiceReportState extends State with TickerPr initialValue: _serviceReport.faultDescription, enabled: false, onSelect: (status) { - _serviceReport.faultDescription = status; - setState(() {}); + if (status != null) { + _serviceReport.faultDescription = status; + setState(() {}); + } }, ), const SizedBox( diff --git a/lib/views/widgets/status/report/fault_desc_menu.dart b/lib/views/widgets/status/report/fault_desc_menu.dart index a2b670de..84de4304 100644 --- a/lib/views/widgets/status/report/fault_desc_menu.dart +++ b/lib/views/widgets/status/report/fault_desc_menu.dart @@ -7,8 +7,9 @@ class FaultDescriptionMenu extends StatefulWidget { final List statuses; final FaultDescription initialStatus; final Function(FaultDescription) onSelect; + final bool enabled; - const FaultDescriptionMenu({Key key, this.statuses, this.onSelect, this.initialStatus}) : super(key: key); + const FaultDescriptionMenu({Key key, this.statuses, this.onSelect, this.initialStatus, this.enabled}) : super(key: key); @override _SingleStatusMenuState createState() => _SingleStatusMenuState(); @@ -71,39 +72,42 @@ class _SingleStatusMenuState extends State { // AppStyle.boxShadow // ] ), - child: DropdownButton( - value: _selectedStatus, - iconSize: 24, - icon: const Icon(Icons.keyboard_arrow_down_rounded), - elevation: 0, - isExpanded: true, - hint: Text( - "Select", - style: Theme.of(context).textTheme.subtitle1, - ), - style: TextStyle(color: Theme.of(context).primaryColor), - underline: SizedBox.shrink(), - onChanged: (FaultDescription newValue) { - setState(() { - _selectedStatus = newValue; - }); - widget.onSelect(newValue); - }, - items: widget.statuses.map>( - (FaultDescription value) { - return DropdownMenuItem( - value: value, - child: Text( - value.defectName ?? "", - style: Theme.of(context).textTheme.subtitle1.copyWith( - color: Theme.of(context).primaryColor, - fontSize: 11, - //fontWeight: FontWeight.bold - ), - ), - ); + child: IgnorePointer( + ignoring: !widget.enabled, + child: DropdownButton( + value: _selectedStatus, + iconSize: 24, + icon: const Icon(Icons.keyboard_arrow_down_rounded), + elevation: 0, + isExpanded: true, + hint: Text( + "Select", + style: Theme.of(context).textTheme.subtitle1, + ), + style: TextStyle(color: Theme.of(context).primaryColor), + underline: SizedBox.shrink(), + onChanged: (FaultDescription newValue) { + setState(() { + _selectedStatus = newValue; + }); + widget.onSelect(newValue); }, - ).toList(), + items: widget.statuses.map>( + (FaultDescription value) { + return DropdownMenuItem( + value: value, + child: Text( + value.defectName ?? "", + style: Theme.of(context).textTheme.subtitle1.copyWith( + color: Theme.of(context).primaryColor, + fontSize: 11, + //fontWeight: FontWeight.bold + ), + ), + ); + }, + ).toList(), + ), ), ); } diff --git a/lib/views/widgets/status/report/service_report_fault_description.dart b/lib/views/widgets/status/report/service_report_fault_description.dart index facda9dd..fc90a3e7 100644 --- a/lib/views/widgets/status/report/service_report_fault_description.dart +++ b/lib/views/widgets/status/report/service_report_fault_description.dart @@ -7,8 +7,6 @@ import 'package:test_sa/models/fault_description.dart'; import 'package:test_sa/views/widgets/loaders/loading_manager.dart'; import 'package:test_sa/views/widgets/status/report/fault_desc_menu.dart'; -import '../../app_text_form_field.dart'; - class ServiceReportFaultDescription extends StatelessWidget { final String requestId; final Function(FaultDescription) onSelect; @@ -29,16 +27,19 @@ class ServiceReportFaultDescription extends StatelessWidget { menuProvider.reset(); await menuProvider.getCallRequestForWorkOrder(user: userProvider.user, host: settingProvider.host, requestId: requestId); }, - child: ((menuProvider.items?.isEmpty ?? true) || (!enabled)) - ? ATextFormField( - initialValue: (!enabled) ? initialValue?.defectName ?? "" : "", - enable: false, - ) - : FaultDescriptionMenu( - initialStatus: initialValue, - statuses: menuProvider.items, - onSelect: onSelect, - ), + child: Builder(builder: (context) { + if (initialValue == null && !enabled && menuProvider.items.isNotEmpty) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + onSelect(menuProvider.items.first); + }); + } + return FaultDescriptionMenu( + initialStatus: initialValue, + statuses: menuProvider.items, + onSelect: onSelect, + enabled: enabled, + ); + }), ); } }