From c63353636a0ff8f6e771d4ffb9e775fb1daa48f2 Mon Sep 17 00:00:00 2001 From: tzw Date: Sat, 2 Mar 2024 18:15:05 +0630 Subject: [PATCH] update carton filter and merge api for shipment --- assets/local/localization_en.json | 8 +- assets/local/localization_mu.json | 4 +- lib/app.dart | 7 +- .../provider/fcs_shipment_data_provider.dart | 2 +- lib/domain/constants.dart | 1 + lib/domain/entities/fcs_shipment.dart | 10 +- lib/domain/entities/shipment_type.dart | 6 +- lib/helpers/api_helper.dart | 45 ++++++--- lib/main-dev.dart | 2 +- lib/pages/carton/carton_editor.dart | 11 --- lib/pages/carton/carton_filter.dart | 77 +++++++++++++-- lib/pages/carton/carton_list.dart | 17 ++-- lib/pages/carton/model/carton_model.dart | 11 ++- .../model/shipment_selection_model.dart | 75 ++++++++++++++ .../carton/package_selection_widget.dart | 24 ++--- lib/pages/carton/widget/shipment_result.dart | 97 +++++++++++++++++++ .../fcs_shipment/fcs_shipment_editor.dart | 17 ++-- lib/pages/fcs_shipment/fcs_shipment_info.dart | 34 +++---- .../model/fcs_shipment_model.dart | 12 ++- lib/pages/user_search/user_list_row.dart | 81 ++++++++-------- lib/pages/user_search/user_search.dart | 7 +- pubspec.yaml | 12 +-- 22 files changed, 410 insertions(+), 150 deletions(-) create mode 100644 lib/pages/carton/model/shipment_selection_model.dart create mode 100644 lib/pages/carton/widget/shipment_result.dart diff --git a/assets/local/localization_en.json b/assets/local/localization_en.json index bd9ad13..cfb7a6d 100644 --- a/assets/local/localization_en.json +++ b/assets/local/localization_en.json @@ -403,14 +403,14 @@ "FCSshipment.process.btn":"Process this shipment", "FCSshipment.arrive.btn":"Arrive this shipment", "FCSshipment.invoiced.btn":"Invoice this shipment", - "FCSshipment.ship.confirm":"Confirm ship?", + "FCSshipment.ship.confirm":"Ship this shipment?", "FCSshipment.cancel.btn":"Cancel this shipment", "FCSshipment.cancel.confirm":"Cancel this shipment?", "FCSshipment.carton":"Cartons", "FCSshipment.package":"Packages", - "FCSshipment.process.confirm":"Confirm process?", - "FCSshipment.arrive.confirm":"Confirm arrive?", - "FCSshipment.invoice.confirm":"Confirm invoice?", + "FCSshipment.process.confirm":"Process this shipment?", + "FCSshipment.arrive.confirm":"Arrive this shipment?", + "FCSshipment.invoice.confirm":"Invoice this shipment?", "FCS Shipment End ================================================================":"", "Shipment Start ================================================================":"", diff --git a/assets/local/localization_mu.json b/assets/local/localization_mu.json index 22ed1a6..b6e416b 100644 --- a/assets/local/localization_mu.json +++ b/assets/local/localization_mu.json @@ -403,7 +403,7 @@ "FCSshipment.process.btn":"လုပ်ဆောင်နေသည်", "FCSshipment.arrive.btn":"ရောက်ရှိသည်", "FCSshipment.invoiced.btn":"ပြေစာယူသည်", - "FCSshipment.ship.confirm":"Confirm ship?", + "FCSshipment.ship.confirm":"Ship this shipment?", "FCSshipment.cancel.btn":"Cancel this shipment", "FCSshipment.cancel.confirm":"Cancel this shipment?", "FCSshipment.popupmenu.all":"All shipments", @@ -413,7 +413,7 @@ "FCSshipment.package":"FCS အထုပ်", "FCSshipment.process.confirm":"လုပ်ငန်းစဉ်ကို အတည်ပြုပါ ?", "FCSshipment.arrive.confirm":"ရောက်ရှိကြောင်း အတည်ပြုပါ ?", - "FCSshipment.invoice.confirm":"ပြေစာအတည်ပြုပါ ?", + "FCSshipment.invoice.confirm":"Invoice this shipment ?", "FCS Shipment End ================================================================":"", "Shipment Start ================================================================":"", diff --git a/lib/app.dart b/lib/app.dart index 7a2085b..d3243fa 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -33,6 +33,7 @@ import 'pages/carton/model/carton_selection_model.dart'; import 'pages/carton/model/consignee_selection_model.dart'; import 'pages/carton/model/package_selection_model.dart'; import 'pages/carton/model/sender_selection_model.dart'; +import 'pages/carton/model/shipment_selection_model.dart'; import 'pages/delivery/model/delivery_model.dart'; class App extends StatefulWidget { @@ -72,6 +73,8 @@ class _AppState extends State { final ConsigneeSelectionModel consigneeSelectionModel = new ConsigneeSelectionModel(); final SenderSelectionModel senderSelectionModel = new SenderSelectionModel(); + final ShipmentSelectionModel shipmentSelectionModel = + new ShipmentSelectionModel(); late AppTranslationsDelegate _newLocaleDelegate; @@ -98,7 +101,8 @@ class _AppState extends State { ..addModel(cartonSelectionModel) ..addModel(packageSelectionModel) ..addModel(consigneeSelectionModel) - ..addModel(senderSelectionModel); + ..addModel(senderSelectionModel) + ..addModel(shipmentSelectionModel); _newLocaleDelegate = AppTranslationsDelegate( newLocale: Translation().supportedLocales().first); @@ -151,6 +155,7 @@ class _AppState extends State { ChangeNotifierProvider.value(value: packageSelectionModel), ChangeNotifierProvider.value(value: consigneeSelectionModel), ChangeNotifierProvider.value(value: senderSelectionModel), + ChangeNotifierProvider.value(value: shipmentSelectionModel), ], child: Consumer( builder: (context, value, child) { diff --git a/lib/data/provider/fcs_shipment_data_provider.dart b/lib/data/provider/fcs_shipment_data_provider.dart index 3954488..d83c976 100644 --- a/lib/data/provider/fcs_shipment_data_provider.dart +++ b/lib/data/provider/fcs_shipment_data_provider.dart @@ -28,7 +28,7 @@ class FcsShipmentDataProvider { } Future cancelFcsShipment(String id) async { - return await requestAPI("/fcs_shipments/cancel", "PUT", + return await requestAPI("/fcs_shipments/cancel", "POST", payload: {"id": id}, token: await getToken()); } diff --git a/lib/domain/constants.dart b/lib/domain/constants.dart index 3b7158f..7cbafdf 100644 --- a/lib/domain/constants.dart +++ b/lib/domain/constants.dart @@ -1,4 +1,5 @@ const uploadPhotoLimit = 10; +const shipmentCountForCartonFilter = 10; const config_collection = "configs"; const user_collection = "users"; diff --git a/lib/domain/entities/fcs_shipment.dart b/lib/domain/entities/fcs_shipment.dart index caeb045..c3a0e67 100644 --- a/lib/domain/entities/fcs_shipment.dart +++ b/lib/domain/entities/fcs_shipment.dart @@ -7,7 +7,7 @@ class FcsShipment { String? shipmentNumber; DateTime? cutoffDate; String? shipmentTypeId; - String? shipType; + String? shipTypeName; DateTime? arrivalDate; DateTime? departureDate; String? consignee; @@ -21,7 +21,7 @@ class FcsShipment { this.shipmentNumber, this.cutoffDate, this.shipmentTypeId, - this.shipType, + this.shipTypeName, this.status, this.arrivalDate, this.departureDate, @@ -42,7 +42,7 @@ class FcsShipment { cutoffDate: _cutoffDate != null ? _cutoffDate.toDate() : null, arrivalDate: _arrivalDate != null ? _arrivalDate.toDate() : null, shipmentNumber: map['shipment_number'], - shipType: map['shipment_type'], + shipTypeName: map['shipment_type_name'], shipmentTypeId: map['shipment_type_id'] ?? "", status: map['status'], consignee: map['consignee'], @@ -53,10 +53,10 @@ class FcsShipment { Map toMap() { return { - "id": id, + 'id': id, 'shipment_number': shipmentNumber, + 'shipment_type_id': shipmentTypeId, 'cutoff_date': cutoffDate?.toUtc().toIso8601String(), - 'shipment_type': shipType, 'arrival_date': arrivalDate?.toUtc().toIso8601String(), 'consignee': consignee, 'port': port, diff --git a/lib/domain/entities/shipment_type.dart b/lib/domain/entities/shipment_type.dart index f40df43..d104435 100644 --- a/lib/domain/entities/shipment_type.dart +++ b/lib/domain/entities/shipment_type.dart @@ -1,10 +1,10 @@ class ShipmentType { String id; - String desc; + String name; - ShipmentType({required this.id, required this.desc}); + ShipmentType({required this.id, required this.name}); factory ShipmentType.fromMap(Map map, String id) { - return ShipmentType(id: id, desc: map['desc'] ?? ""); + return ShipmentType(id: id, name: map['name'] ?? ""); } } diff --git a/lib/helpers/api_helper.dart b/lib/helpers/api_helper.dart index 5cab2ec..6839e45 100644 --- a/lib/helpers/api_helper.dart +++ b/lib/helpers/api_helper.dart @@ -11,8 +11,9 @@ import 'package:path_provider/path_provider.dart'; import '../config.dart'; import 'dev_info.dart'; import 'firebase_helper.dart'; +import 'dart:developer' as log; -final log = Logger('requestAPI'); +final logger = Logger('requestAPI'); // request makes http request // if token is null @@ -21,7 +22,7 @@ Future requestAPI(String path, method, DevInfo devInfo = await DevInfo.getDevInfo(); String deviceName = "${devInfo.model}(${devInfo.id})"; - log.info("device:${devInfo.deviceID},deviceName:$deviceName"); + logger.info("device:${devInfo.deviceID},deviceName:$deviceName"); Map headers = {}; if (token != null) { @@ -39,9 +40,21 @@ Future requestAPI(String path, method, receiveTimeout: Duration(milliseconds: networkTimeout ?? 50000), headers: headers, ); - log.info("baseUrl:${options.baseUrl}, path:$path"); - log.info("payload:$payload"); - + logger.info("baseUrl:${options.baseUrl}, path:$path, method:$method"); + logger.info("payload:$payload"); + + List list = []; + + headers.forEach((key, value) { + var r = '-H $key:$value '; + list.add(r); + }); + + String curlPath = + "curl -X $method ${list.join("")}${payload != null ? '-d $payload ' : ''}${options.baseUrl}$path"; + + log.log(curlPath); + try { Dio dio = new Dio(options); Response response = await dio.request( @@ -55,7 +68,7 @@ Future requestAPI(String path, method, throw Exception(data.message); } } catch (e) { - log.warning("path:$path, api:$e"); + logger.warning("path:$path, api:$e"); throw e; } } @@ -67,7 +80,7 @@ Future requestDownloadAPI(String path, method, DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; String deviceName = "${androidInfo.model}(${androidInfo.id})"; - log.info("device:${androidInfo.id},deviceName:$deviceName"); + logger.info("device:${androidInfo.id},deviceName:$deviceName"); var bytes = utf8.encode(payload); var base64Str = base64.encode(bytes); @@ -75,7 +88,7 @@ Future requestDownloadAPI(String path, method, try { String baseUrl = url == null ? Config.instance.apiURL : url; - log.info("Path:$baseUrl$path"); + logger.info("Path:$baseUrl$path"); HttpClient client = new HttpClient(); var _downloadData = StringBuffer(); var fileSave = new File(filePath!); @@ -96,7 +109,7 @@ Future requestDownloadAPI(String path, method, fileSave.writeAsString(_downloadData.toString()); }); } catch (e) { - log.warning("path:$path, api:$e"); + logger.warning("path:$path, api:$e"); throw e; } } @@ -108,7 +121,7 @@ Future requestDownloadPDFAPI(String path, method, DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; String deviceName = "${androidInfo.model}(${androidInfo.id})"; - log.info("device:${androidInfo.id},deviceName:$deviceName"); + logger.info("device:${androidInfo.id},deviceName:$deviceName"); var bytes = utf8.encode(payload); var base64Str = base64.encode(bytes); @@ -116,7 +129,7 @@ Future requestDownloadPDFAPI(String path, method, try { String baseUrl = url == null ? Config.instance.apiURL : url; - log.info("Path:$baseUrl$path"); + logger.info("Path:$baseUrl$path"); HttpClient client = new HttpClient(); // var _downloadData = StringBuffer(); var fileSave = new File(filePath!); @@ -140,7 +153,7 @@ Future requestDownloadPDFAPI(String path, method, // fileSave.writeAsString(_downloadData.toString()); // }); } catch (e) { - log.warning("path:$path, api:$e"); + logger.warning("path:$path, api:$e"); throw e; } } @@ -157,7 +170,7 @@ Future requestDownload(String path, method, DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; String deviceName = "${androidInfo.model}(${androidInfo.id})"; - log.info("device:${androidInfo.id},deviceName:$deviceName"); + logger.info("device:${androidInfo.id},deviceName:$deviceName"); var bytes = utf8.encode(payload); var base64Str = base64.encode(bytes); @@ -165,7 +178,7 @@ Future requestDownload(String path, method, try { String baseUrl = url == null ? Config.instance.apiURL : url; - log.info("Path:$baseUrl$path"); + logger.info("Path:$baseUrl$path"); HttpClient client = new HttpClient(); // var _downloadData = StringBuffer(); var request = await client.postUrl(Uri.parse("$baseUrl$path")); @@ -207,7 +220,7 @@ Future requestDownload(String path, method, }); } catch (e) { e.toString(); - log.warning("path:$path, api:$e"); + logger.warning("path:$path, api:$e"); throw e; } } @@ -218,7 +231,7 @@ Future downloadPDF( final directory = await getApplicationSupportDirectory(); String path = ('${directory.path}'); - log.info("download file path:$path"); + logger.info("download file path:$path"); var data = {"template_name": templateName, "data": values}; print("payload:$data"); diff --git a/lib/main-dev.dart b/lib/main-dev.dart index f1fd3bf..e3e1efe 100644 --- a/lib/main-dev.dart +++ b/lib/main-dev.dart @@ -16,7 +16,7 @@ Future main() async { flavor: Flavor.DEV, color: Colors.blue, apiURL: "https://asia-northeast1-fcs-dev1.cloudfunctions.net/API12", - reportURL: "http://petrok.mokkon.com:8091", + reportURL: "http://petrok.mokkon.com:7071", reportProjectID: "fcs-dev", bucketName: "gs://fcs-dev1-us", level: Level.ALL); diff --git a/lib/pages/carton/carton_editor.dart b/lib/pages/carton/carton_editor.dart index 3df8d7f..25325e0 100644 --- a/lib/pages/carton/carton_editor.dart +++ b/lib/pages/carton/carton_editor.dart @@ -63,17 +63,6 @@ class _CartonEditorState extends State { _isNew = true; _selectedCartonType = carton_from_packages; _cartons = []; - _sender = User( - name: "ptd-phyo44 kaelone", - fcsID: "FCS-8X6V", - phoneNumber: "+959444444444", - id: "48u_4s-HiQeW-HwSqeRd9TSMWh3mLZfSk5rpaUEh_zw"); - - _consignee = User( - id: "HsIwG88K-0_HSazgEy5QR27kcjkOvfv7_Sr1JP18Q1A", - name: "One One", - phoneNumber: "+959111111111", - fcsID: "FCS-EFRF"); } if (mounted) { setState(() {}); diff --git a/lib/pages/carton/carton_filter.dart b/lib/pages/carton/carton_filter.dart index 9fc0054..48a0af6 100644 --- a/lib/pages/carton/carton_filter.dart +++ b/lib/pages/carton/carton_filter.dart @@ -3,11 +3,14 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../domain/constants.dart'; +import '../../domain/entities/fcs_shipment.dart'; import '../../domain/entities/user.dart'; import '../../helpers/theme.dart'; import '../../localization/app_translations.dart'; import '../main/util.dart'; import '../widgets/local_text.dart'; +import 'model/shipment_selection_model.dart'; +import 'widget/shipment_result.dart'; import 'widget/user_search_result.dart'; import 'model/carton_model.dart'; import 'model/consignee_selection_model.dart'; @@ -24,6 +27,8 @@ class _CartonFilterState extends State { String? _selectedStatus; User? _selectedSender; User? _selectedConsignee; + FcsShipment? _selectedShipment; + bool _isLoadMoreShipment = false; List statusList = [ all_status, @@ -48,11 +53,13 @@ class _CartonFilterState extends State { _selectedStatus = model.filterByStatus ?? all_status; _selectedConsignee = model.filterByConsingee ?? User(id: all, name: "All"); _selectedSender = model.filterBySender ?? User(id: all, name: "All"); + _selectedShipment = model.shipment ?? model.defaultShipment; init(); super.initState(); } init() async { + _initShipment(); _initConsignee(); _initSender(); if (mounted) { @@ -60,6 +67,13 @@ class _CartonFilterState extends State { } } + _initShipment() async { + await context.read().refresh(); + if (mounted) { + setState(() {}); + } + } + _initConsignee() { var model = context.read(); _consigneeCtl.text = model.query; @@ -109,6 +123,21 @@ class _CartonFilterState extends State { }); } + Future _loadMoreShipment() async { + if (_isLoadMoreShipment) return; + var model = context.read(); + if (model.ended) return; + setState(() { + _isLoadMoreShipment = true; + }); + + await model.loadMoreData(); + + setState(() { + _isLoadMoreShipment = false; + }); + } + Future _loadMoreSender() async { if (_isLoadMoreSender) return; var model = context.read(); @@ -130,9 +159,11 @@ class _CartonFilterState extends State { @override Widget build(BuildContext context) { var consigneeModel = context.watch(); - List consignees = consigneeModel.getConsginees; var senderModel = context.watch(); + var shipmentModel = context.watch(); + List consignees = consigneeModel.getConsginees; List senders = senderModel.getSenders; + List shipments = shipmentModel.getShipments; final _titleWidget = SizedBox( height: 30, @@ -144,6 +175,9 @@ class _CartonFilterState extends State { labelPadding: const EdgeInsets.all(0), indicatorPadding: const EdgeInsets.all(0), tabs: [ + Tab( + text: AppTranslations.of(context)?.text("box.fcs_shipment_num"), + ), Tab( text: AppTranslations.of(context)?.text("box.consignee.title"), ), @@ -157,6 +191,30 @@ class _CartonFilterState extends State { ), ); + final _shipmentWidget = Column( + children: [ + Expanded( + child: ShipmentResult( + searchResults: shipments, + isLoading: shipmentModel.isLoading, + noDataLabelKey: 'user.no_consignee', + selectedUser: _selectedShipment, + controller: widget.controller, + isLoadingMore: _isLoadMoreShipment, + onLoadMore: _loadMoreShipment, + onRefresh: () async { + _initShipment(); + }, + onTap: (a) async { + setState(() { + _selectedShipment = a; + }); + }, + ), + ) + ], + ); + final _consingeeWidget = Column( children: [ const SizedBox(height: 15), @@ -335,13 +393,14 @@ class _CartonFilterState extends State { ), Expanded( child: DefaultTabController( - length: 3, + length: 4, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _titleWidget, Expanded( child: TabBarView(children: [ + _shipmentWidget, _consingeeWidget, _senderWidget, _statusWidget @@ -428,14 +487,16 @@ class _CartonFilterState extends State { _clearFilter() async { await context.read().filterCarton( - User(id: all, name: "All"), User(id: all, name: "All"), all_status); - Navigator.pop(context); + FcsShipment(shipmentNumber: "All shipments", id: all), + User(id: all, name: "All"), + User(id: all, name: "All"), + all_status); + Navigator.pop(context, true); } _filter() async { - await context - .read() - .filterCarton(_selectedConsignee, _selectedSender, _selectedStatus); - Navigator.pop(context); + await context.read().filterCarton(_selectedShipment, + _selectedConsignee, _selectedSender, _selectedStatus); + Navigator.pop(context, true); } } diff --git a/lib/pages/carton/carton_list.dart b/lib/pages/carton/carton_list.dart index 4001489..6cd36b0 100644 --- a/lib/pages/carton/carton_list.dart +++ b/lib/pages/carton/carton_list.dart @@ -8,8 +8,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:provider/provider.dart'; - -import '../../domain/constants.dart'; import '../../domain/entities/carton.dart'; import '../../pagination/paginator_listview.dart'; import '../carton_search/carton_search.dart'; @@ -38,10 +36,9 @@ class _CartonListState extends State { _init() async { var model = context.read(); - _shipments = await context.read().getAllShipments(); - _shipments.insert(0, FcsShipment(shipmentNumber: "All Shipments", id: all)); - _selectedShipment = - model.shipment ?? FcsShipment(shipmentNumber: "All Shipments", id: all); + _shipments = await context.read().getShipments(); + _shipments.insert(0, model.defaultShipment); + _selectedShipment = model.shipment ?? model.defaultShipment; model.loadPaginationCartons(); if (mounted) { @@ -309,7 +306,7 @@ class _CartonListState extends State { ], ), onPressed: () async { - await showModalBottomSheet( + bool? updated = await showModalBottomSheet( context: context, useRootNavigator: true, isScrollControlled: true, @@ -323,6 +320,12 @@ class _CartonListState extends State { expand: false, builder: (_, controller) => CartonFilter(controller: controller))); + var model = context.read(); + if (updated ?? false) { + setState(() { + _selectedShipment = model.shipment ?? model.defaultShipment; + }); + } }); } diff --git a/lib/pages/carton/model/carton_model.dart b/lib/pages/carton/model/carton_model.dart index fe98399..3d91222 100644 --- a/lib/pages/carton/model/carton_model.dart +++ b/lib/pages/carton/model/carton_model.dart @@ -17,6 +17,8 @@ import 'package:path/path.dart' as Path; class CartonModel extends BaseModel { final log = Logger('CartonModel'); + var defaultShipment =FcsShipment(shipmentNumber: "All shipments", id: all); + PaginatorListener? cartonsByFilter; PaginatorListener? getBoxes; @@ -56,7 +58,8 @@ class CartonModel extends BaseModel { _loadPaginationCartons(); } - filterCarton(User? consignee, User? sender, String? status) async { + filterCarton(FcsShipment? fcsShipment, User? consignee, User? sender, + String? status) async { filterByStatus = status; if (status == all_status) { @@ -77,6 +80,12 @@ class CartonModel extends BaseModel { filterBySender = sender; } + if (fcsShipment?.id == all) { + shipment = null; + } else { + shipment = fcsShipment; + } + loadPaginationCartons(); notifyListeners(); } diff --git a/lib/pages/carton/model/shipment_selection_model.dart b/lib/pages/carton/model/shipment_selection_model.dart new file mode 100644 index 0000000..4af29e9 --- /dev/null +++ b/lib/pages/carton/model/shipment_selection_model.dart @@ -0,0 +1,75 @@ +import 'dart:async'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:fcs/domain/entities/fcs_shipment.dart'; +import 'package:logging/logging.dart'; + +import '../../../domain/constants.dart'; +import '../../main/model/base_model.dart'; + +class ShipmentSelectionModel extends BaseModel { + final log = Logger("ShipmentSelectionModel"); + + List _shipments = []; + + List get getShipments { + var list = new List.from(_shipments); + return list + ..insert(0, FcsShipment(id: all, shipmentNumber: "All shipments")); + } + + bool isLoading = false; + DocumentSnapshot? _lastDocument; + bool ended = false; + + Future refresh() async { + _shipments.clear(); + _lastDocument = null; + ended = false; + await loadMoreData(); + notifyListeners(); + } + + Future loadMoreData() async { + int rowPerPage = 20; + + try { + isLoading = true; + String path = "/$fcs_shipment_collection"; + Query query = FirebaseFirestore.instance + .collection(path) + .where("status", whereIn: [ + fcs_shipment_processing_status, + fcs_shipment_shipped_status, + fcs_shipment_arrived_status, + fcs_shipment_invoiced_status + ]) + .where("is_deleted", isEqualTo: false) + .orderBy("update_time", descending: true); + + if (_lastDocument != null) { + query = query.startAfterDocument(_lastDocument!); + } + + QuerySnapshot querySnap = await query.limit(rowPerPage).get(); + + if (querySnap.docs.isEmpty) return; + _lastDocument = querySnap.docs[querySnap.docs.length - 1]; + + List list = querySnap.docs.map((documentSnapshot) { + var p = FcsShipment.fromMap( + documentSnapshot.data() as Map, + documentSnapshot.id); + return p; + }).toList(); + + _shipments.addAll(list); + if (list.length < rowPerPage) ended = true; + notifyListeners(); + } catch (e) { + log.warning("error:$e"); + } finally { + isLoading = false; + } + } +} diff --git a/lib/pages/carton/package_selection_widget.dart b/lib/pages/carton/package_selection_widget.dart index c6c501b..e84c96f 100644 --- a/lib/pages/carton/package_selection_widget.dart +++ b/lib/pages/carton/package_selection_widget.dart @@ -13,7 +13,6 @@ import '../widgets/continue_button.dart'; import '../widgets/display_text.dart'; import '../widgets/local_title.dart'; import '../widgets/previous_button.dart'; -import 'model/carton_selection_model.dart'; import 'model/package_selection_model.dart'; import 'package_selection_result.dart'; @@ -63,25 +62,26 @@ class _PackageSelectionWidgetState extends State { _init() { var searchModel = context.read(); + searchModel.addDefaultPackages( shipmentId: widget.shipment.id!, consigneeId: widget.consignee.id!, senderId: widget.sender.id!); searchModel.addSelectedPackage(widget.packages); - _controller.text = searchModel.query; _query = searchModel.query; + if (mounted) { setState(() {}); } } - @override - void didUpdateWidget(covariant PackageSelectionWidget oldWidget) { - _init(); - super.didUpdateWidget(oldWidget); - } + // @override + // void didUpdateWidget(covariant PackageSelectionWidget oldWidget) { + // _init(); + // super.didUpdateWidget(oldWidget); + // } Future _loadMoreData() async { if (_isLoadMore) return; @@ -278,7 +278,7 @@ class _PackageSelectionWidgetState extends State { isLoadingMore: _isLoadMore, onLoadMore: _loadMoreData, onRefresh: () async { - _init(); + _search(); setState(() { _down = true; }); @@ -330,9 +330,11 @@ class _PackageSelectionWidgetState extends State { _search({bool imm = false}) async { try { - await context - .read() - .search(_query, imm: imm, shipmentId: widget.shipment.id!); + await context.read().search(_query, + imm: imm, + shipmentId: widget.shipment.id!, + senderId: widget.sender.id!, + consigneeId: widget.consignee.id!); } catch (e) { showMsgDialog(context, 'Error', e.toString()); } diff --git a/lib/pages/carton/widget/shipment_result.dart b/lib/pages/carton/widget/shipment_result.dart new file mode 100644 index 0000000..8c33076 --- /dev/null +++ b/lib/pages/carton/widget/shipment_result.dart @@ -0,0 +1,97 @@ +import 'package:fcs/domain/entities/fcs_shipment.dart'; +import 'package:fcs/pages/widgets/local_text.dart'; +import 'package:flutter/material.dart'; + +import '../../../../helpers/theme.dart'; + +typedef OnAction = Future Function(); + +class ShipmentResult extends StatelessWidget { + final bool isLoadingMore; + final OnAction onLoadMore; + final OnAction onRefresh; + final Function(FcsShipment)? onTap; + final ScrollController controller; + final FcsShipment? selectedUser; + final List searchResults; + final String? noDataLabelKey; + final bool isLoading; + + const ShipmentResult( + {super.key, + required this.isLoadingMore, + required this.onLoadMore, + required this.onRefresh, + this.onTap, + required this.controller, + this.selectedUser, + this.searchResults = const [], + this.noDataLabelKey, + this.isLoading = false}); + + bool _scrollNotification(ScrollNotification scrollInfo) { + if (!isLoadingMore && + scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) { + onLoadMore(); + } + return true; + } + + @override + Widget build(BuildContext context) { + return searchResults.isEmpty && !isLoading + ? noDataLabelKey == null + ? const SizedBox() + : Center( + child: LocalText(context, noDataLabelKey!, + color: Colors.black, fontSize: 15)) + : Column(children: [ + Expanded( + child: NotificationListener( + onNotification: _scrollNotification, + child: RefreshIndicator( + color: primaryColor, + onRefresh: () => onRefresh(), + child: ListView.builder( + controller: controller, + shrinkWrap: true, + physics: const AlwaysScrollableScrollPhysics(), + itemBuilder: (context, index) { + FcsShipment user = searchResults[index]; + + return ListTile( + onTap: () { + if (onTap != null) { + onTap!(user); + } + }, + title: Row( + children: [ + Text(user.shipmentNumber ?? "", + style: const TextStyle( + fontSize: 15, color: Colors.black)), + const SizedBox( + width: 20, + ), + selectedUser?.id == user.id + ? const Icon( + Icons.check, + color: Colors.grey, + ) + : const SizedBox() + ], + ), + ); + }, + itemCount: searchResults.length)), + )), + Container( + height: isLoadingMore ? 50.0 : 0, + color: Colors.transparent, + child: const Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(primaryColor)), + )), + ]); + } +} diff --git a/lib/pages/fcs_shipment/fcs_shipment_editor.dart b/lib/pages/fcs_shipment/fcs_shipment_editor.dart index 6719676..e430131 100644 --- a/lib/pages/fcs_shipment/fcs_shipment_editor.dart +++ b/lib/pages/fcs_shipment/fcs_shipment_editor.dart @@ -64,7 +64,11 @@ class _FcsShipmentEditorState extends State { _portController.text = _shipment.port ?? ""; _destinationController.text = _shipment.destination ?? ""; - // _currentShipmentType = model.shipmentTypes.where((e) => e.id == _shipment.shipmentTypeId).first; + List list = model.shipmentTypes + .where((e) => e.id == _shipment.shipmentTypeId) + .toList(); + + _currentShipmentType = list.isNotEmpty ? list.first : null; } else { _currentShipmentType = model.shipmentTypes[0]; } @@ -175,7 +179,7 @@ class _FcsShipmentEditorState extends State { icon: Icon(Ionicons.ios_airplane, color: primaryColor)), items: shipmentTypes .map((e) => - DropdownMenuItem(child: Text(e.desc), value: e)) + DropdownMenuItem(child: Text(e.name), value: e)) .toList(), onChanged: (selected) => { setState(() { @@ -209,7 +213,7 @@ class _FcsShipmentEditorState extends State { FcsShipment fcsShipment = FcsShipment(); fcsShipment.id = _shipment.id; fcsShipment.shipmentNumber = _shipmentNumberController.text; - fcsShipment.shipmentTypeId = _currentShipmentType?.id; + fcsShipment.shipmentTypeId = _currentShipmentType?.id ?? ""; fcsShipment.consignee = _consigneeController.text; fcsShipment.port = _portController.text; fcsShipment.destination = _destinationController.text; @@ -230,9 +234,8 @@ class _FcsShipmentEditorState extends State { setState(() { _isLoading = true; }); - var shipmentModel = Provider.of(context, listen: false); try { - await shipmentModel.create(fcsShipment); + await context.read().create(fcsShipment); Navigator.pop(context); } catch (e) { showMsgDialog(context, "Error", e.toString()); @@ -250,9 +253,9 @@ class _FcsShipmentEditorState extends State { setState(() { _isLoading = true; }); - var shipmentModel = Provider.of(context, listen: false); + try { - await shipmentModel.update(fcsShipment); + await context.read().update(fcsShipment); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); diff --git a/lib/pages/fcs_shipment/fcs_shipment_info.dart b/lib/pages/fcs_shipment/fcs_shipment_info.dart index e51cd00..5cfa9b5 100644 --- a/lib/pages/fcs_shipment/fcs_shipment_info.dart +++ b/lib/pages/fcs_shipment/fcs_shipment_info.dart @@ -59,7 +59,7 @@ class _FcsShipmentInfoState extends State { if (_fcsShipment?.departureDate != null) _departureDateControler.text = dateFormatter.format(_fcsShipment!.departureDate!); - _shipmentTypeControler.text = _fcsShipment!.shipType ?? ""; + _shipmentTypeControler.text = _fcsShipment!.shipTypeName ?? ""; _consigneeController.text = _fcsShipment!.consignee ?? ""; _portController.text = _fcsShipment!.port ?? ""; _destinationController.text = _fcsShipment!.destination ?? ""; @@ -148,7 +148,7 @@ class _FcsShipmentInfoState extends State { final invoiceBtn = Padding( padding: const EdgeInsets.symmetric(horizontal: 30), child: LocalButton( - textKey: "FCSshipment.invoice.btn", + textKey: "FCSshipment.invoiced.btn", callBack: _invoice, ), ); @@ -194,19 +194,17 @@ class _FcsShipmentInfoState extends State { Row(mainAxisAlignment: MainAxisAlignment.end, children: [ Expanded( child: cutoffDateDBox, - flex: 2, - ), - Flexible( - child: etaBox, + // flex: 2, ), + Flexible(child: etaBox), ]), Row( children: [ Expanded( child: cartonBox, - flex: 2, + // flex: 2, ), - Flexible(child: packageBox), + Flexible(child: packageBox) ], ), shipTypeBox, @@ -296,9 +294,7 @@ class _FcsShipmentInfoState extends State { _isLoading = true; }); try { - FcsShipmentModel fcsShipmentModel = - Provider.of(context, listen: false); - await fcsShipmentModel.process(_fcsShipment!.id!); + await context.read().process(_fcsShipment!.id!); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); @@ -320,9 +316,7 @@ class _FcsShipmentInfoState extends State { _isLoading = true; }); try { - FcsShipmentModel fcsShipmentModel = - Provider.of(context, listen: false); - await fcsShipmentModel.ship(_fcsShipment!.id!); + await context.read().ship(_fcsShipment!.id!); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); @@ -344,9 +338,7 @@ class _FcsShipmentInfoState extends State { _isLoading = true; }); try { - FcsShipmentModel fcsShipmentModel = - Provider.of(context, listen: false); - await fcsShipmentModel.arrive(_fcsShipment!.id!); + await context.read().arrive(_fcsShipment!.id!); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); @@ -368,9 +360,7 @@ class _FcsShipmentInfoState extends State { _isLoading = true; }); try { - FcsShipmentModel fcsShipmentModel = - Provider.of(context, listen: false); - await fcsShipmentModel.invoice(_fcsShipment!.id!); + await context.read().invoice(_fcsShipment!.id!); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); @@ -386,9 +376,7 @@ class _FcsShipmentInfoState extends State { _isLoading = true; }); try { - FcsShipmentModel fcsShipmentModel = - Provider.of(context, listen: false); - await fcsShipmentModel.cancel(_fcsShipment!.id!); + await context.read().cancel(_fcsShipment!.id!); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); diff --git a/lib/pages/fcs_shipment/model/fcs_shipment_model.dart b/lib/pages/fcs_shipment/model/fcs_shipment_model.dart index b90cc93..2bb4022 100644 --- a/lib/pages/fcs_shipment/model/fcs_shipment_model.dart +++ b/lib/pages/fcs_shipment/model/fcs_shipment_model.dart @@ -87,7 +87,7 @@ class FcsShipmentModel extends BaseModel { try { var snaps = await FirebaseFirestore.instance .collection("/$fcs_shipment_collection") - // .where("status", isEqualTo: fcs_shipment_confirmed_status) + .where("status", isEqualTo: fcs_shipment_processing_status) .get(const GetOptions(source: Source.server)); fcsShipments = snaps.docs.map((documentSnapshot) { var fcs = @@ -199,12 +199,20 @@ class FcsShipmentModel extends BaseModel { return Services.instance.fcsShipmentService.report(fcsShipment); } - Future> getAllShipments() async { + Future> getShipments() async { List fcsShipments = []; try { var snaps = await FirebaseFirestore.instance .collection("/$fcs_shipment_collection") + .where("status", whereIn: [ + fcs_shipment_processing_status, + fcs_shipment_shipped_status, + fcs_shipment_arrived_status, + fcs_shipment_invoiced_status + ]) .where("is_deleted", isEqualTo: false) + .orderBy("update_time", descending: true) + .limit(shipmentCountForCartonFilter) .get(const GetOptions(source: Source.server)); fcsShipments = snaps.docs.map((documentSnapshot) { var fcs = diff --git a/lib/pages/user_search/user_list_row.dart b/lib/pages/user_search/user_list_row.dart index ffdc859..635d24e 100644 --- a/lib/pages/user_search/user_list_row.dart +++ b/lib/pages/user_search/user_list_row.dart @@ -21,48 +21,51 @@ class UserListRow extends StatelessWidget { onTap: () { if (onUserRowSelect != null) onUserRowSelect!(user); }, - child: Row( - children: [ - Expanded( - child: new Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: new Row( - children: [ - new Padding( - padding: new EdgeInsets.symmetric( - horizontal: 32.0 - dotSize / 2), - child: Icon( - Icons.perm_identity, - color: primaryColor, - size: 50, - )), - new Expanded( - child: new Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - new Text( - user.name ?? "", - style: new TextStyle( - fontSize: 15.0, color: Colors.black), - ), - new Text( - user.fcsID ?? "", - style: new TextStyle( - fontSize: 13.0, color: Colors.grey), - ), - new Text( - user.phoneNumber ?? "", - style: new TextStyle( - fontSize: 13.0, color: Colors.grey), - ), - ], + child: Padding( + padding: const EdgeInsets.only(left: 15, right: 15), + child: Row( + children: [ + Expanded( + child: new Padding( + padding: const EdgeInsets.symmetric(vertical: 13.0), + child: new Row( + children: [ + Icon( + Icons.perm_identity, + color: primaryColor, + size: 30, ), - ), - ], + new Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 15), + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Text( + user.name ?? "", + style: new TextStyle( + fontSize: 15.0, color: Colors.black), + ), + new Text( + user.fcsID ?? "", + style: new TextStyle( + fontSize: 14.0, color: Colors.grey), + ), + new Text( + user.phoneNumber ?? "", + style: new TextStyle( + fontSize: 14.0, color: Colors.grey), + ), + ], + ), + ), + ), + ], + ), ), ), - ), - ], + ], + ), ), ), ), diff --git a/lib/pages/user_search/user_search.dart b/lib/pages/user_search/user_search.dart index e892d03..ea88c4e 100644 --- a/lib/pages/user_search/user_search.dart +++ b/lib/pages/user_search/user_search.dart @@ -6,6 +6,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../../data/services/services.dart'; + typedef OnUserSelect(User suer); typedef OnUserRowSelect(User suer); @@ -124,9 +126,10 @@ class UserSearchDelegate extends SearchDelegate { ); } - _onUserRowSelect(BuildContext context, User user) { + _onUserRowSelect(BuildContext context, User user) async { + User? u = await Services.instance.userService.getUser(user.id ?? ""); if (onUserSelect != null) { - onUserSelect!(user); + onUserSelect!(u ?? user); } if (popPage) { Navigator.pop(context); diff --git a/pubspec.yaml b/pubspec.yaml index fe2b945..c6132dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,11 +14,11 @@ dependencies: cupertino_icons: ^1.0.3 - firebase_core: ^2.17.0 - firebase_auth: ^4.10.1 - cloud_firestore: ^4.9.3 - firebase_storage: ^11.5.6 - firebase_messaging: ^14.6.9 + firebase_core: ^2.25.5 + firebase_auth: ^4.17.6 + cloud_firestore: ^4.15.6 + firebase_storage: ^11.6.7 + firebase_messaging: ^14.7.17 provider: ^6.0.0 image_picker: ^1.0.6 @@ -49,7 +49,7 @@ dependencies: timeline_list: ^0.0.5 barcode_scan2: ^4.1.4 flutter_pdfview: ^1.2.1 - flutter_local_notifications: ^16.3.0 + flutter_local_notifications: ^16.3.2 share: ^2.0.4 cached_network_image: ^3.3.1 flutter_cache_manager: ^3.1.2