From 56ec7dd9a0cf60b9806b9a77801af307efb79667 Mon Sep 17 00:00:00 2001 From: tzw Date: Tue, 24 Sep 2024 08:23:03 +0630 Subject: [PATCH 1/5] merge --- lib/pages/carton/model/carton_model.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/carton/model/carton_model.dart b/lib/pages/carton/model/carton_model.dart index 14d948a..95a2e97 100644 --- a/lib/pages/carton/model/carton_model.dart +++ b/lib/pages/carton/model/carton_model.dart @@ -236,6 +236,7 @@ class CartonModel extends BaseModel { } Future> searchCarton(String term) async { + return Services.instance.cartonService.searchCarton(term); } From 02e079c5145e821cb27fb7c4dcdca391221ade38 Mon Sep 17 00:00:00 2001 From: tzw Date: Wed, 25 Sep 2024 21:49:09 +0630 Subject: [PATCH 2/5] update cargo type form from rate, update carton info and form --- assets/local/localization_en.json | 10 +- assets/local/localization_mu.json | 6 +- lib/constants.dart | 13 +- lib/data/provider/carton_data_provider.dart | 23 +- lib/data/provider/rate_data_provider.dart | 1 + lib/data/services/carton_imp.dart | 15 ++ lib/data/services/carton_service.dart | 3 + lib/domain/entities/cargo_type.dart | 35 ++- lib/domain/entities/carton.dart | 201 +++++++-------- lib/domain/entities/fcs_shipment.dart | 72 +++--- lib/domain/entities/shipment_consignee.dart | 17 ++ lib/domain/entities/shipment_port.dart | 17 ++ lib/domain/entities/shipment_type.dart | 6 + lib/domain/vo/local_popupmenu.dart | 2 +- lib/helpers/api_helper.dart | 2 +- lib/helpers/pdf.dart | 2 +- lib/main-dev.dart | 3 +- lib/pages/carton/cargo_type_addition.dart | 114 +++++++++ lib/pages/carton/cargo_widget.dart | 85 +++++-- lib/pages/carton/carton_editor.dart | 22 +- lib/pages/carton/carton_info.dart | 114 +++++---- lib/pages/carton/carton_package_editor.dart | 98 ++++++-- lib/pages/carton/carton_package_form.dart | 48 +++- lib/pages/carton/carton_size_widget.dart | 56 +++-- lib/pages/carton/carton_submit.dart | 13 +- lib/pages/carton/custom_duty_addition.dart | 131 ---------- .../carton/mix_carton/mix_carton_editor.dart | 72 +++++- .../carton/mix_carton/mix_carton_form.dart | 31 ++- .../carton/mix_carton/mix_carton_submit.dart | 7 +- lib/pages/carton/mix_carton/type_widget.dart | 28 ++- lib/pages/carton/model/carton_model.dart | 48 ++-- .../carton/model/carton_selection_model.dart | 2 +- lib/pages/carton/print_qr_code_page.dart | 7 +- lib/pages/carton/surcharge_item_addition.dart | 114 +++++++++ lib/pages/carton/widget/carton_list_row.dart | 2 +- lib/pages/carton_search/carton_list_row.dart | 2 +- lib/pages/carton_search/carton_search.dart | 32 ++- lib/pages/delivery/delivery_info.dart | 8 +- lib/pages/delivery/delivery_list_row.dart | 2 +- .../fcs_shipment/fcs_shipment_editor.dart | 231 ++++++++++++------ lib/pages/fcs_shipment/fcs_shipment_info.dart | 62 ++--- lib/pages/fcs_shipment/fcs_shipment_list.dart | 34 +-- .../model/fcs_shipment_model.dart | 81 +++++- lib/pages/package/model/package_model.dart | 3 + lib/pages/package_search/package_search.dart | 34 ++- lib/pages/pickup/model/pickup_model.dart | 5 + lib/pages/pickup_search/pickup_search.dart | 33 ++- lib/pages/rates/cargo_editor.dart | 81 +++++- lib/pages/user_search/user_search.dart | 2 +- lib/pagination/paginator_listview.dart | 19 +- pubspec.yaml | 1 + 51 files changed, 1407 insertions(+), 643 deletions(-) create mode 100644 lib/domain/entities/shipment_consignee.dart create mode 100644 lib/domain/entities/shipment_port.dart create mode 100644 lib/pages/carton/cargo_type_addition.dart delete mode 100644 lib/pages/carton/custom_duty_addition.dart create mode 100644 lib/pages/carton/surcharge_item_addition.dart diff --git a/assets/local/localization_en.json b/assets/local/localization_en.json index c093ec8..0c3a1e9 100644 --- a/assets/local/localization_en.json +++ b/assets/local/localization_en.json @@ -351,7 +351,7 @@ "box.no_package":"There is no packages.", "box.input_cargo_weight":"Cargo type (lb)", "box.input_surcharge_item":"Surcharge items", - "box.select.cargo_type":"Select surcharge item", + "box.select.cargo_type":"Add surcharge item", "box.surcharge.item":"Surcharge items", "box.package.count":"Packages ({0})", "box.bill_to":"Bill to", @@ -370,6 +370,7 @@ "box.print.btn":"Print", "box.delivery":"Delivery", "box.pickup":"Pick-up", + "box.add.cargo_type":"Add cargo type", "Boxes End ================================================================":"", "Delivery Start ================================================================":"", @@ -393,8 +394,8 @@ "FCSshipment.departure_date":"Departure date", "FCSshipment.shipment_type":"Shipment type", "FCSshipment.consignee":"Consignee", - "FCSshipment.port_of_loading":"Port of loading", - "FCSshipment.final_destination":"Final destination", + "FCSshipment.port_of_loading":"Loading port", + "FCSshipment.final_destination":"Destination port", "FCSshipment.status":"Status", "FCSshipment.remark":"Remark", "FCSshipment.commercial_invoice":"Download commercial invoice", @@ -415,6 +416,7 @@ "FCSshipment.process.confirm":"Process this shipment?", "FCSshipment.arrive.confirm":"Arrive this shipment?", "FCSshipment.invoice.confirm":"Invoice this shipment?", + "FCSshipment.delete.confirm":"Delete this shipment?", "FCS Shipment End ================================================================":"", "Shipment Start ================================================================":"", @@ -509,6 +511,8 @@ "cargo.qty":"Quantity", "cargo.amount":"Amount", "cargo.edit.delete.confirm":"Delete this cargo type?", + "cargo.display_index":"Display index", + "cargo.is_default":"Default Cargo", "Cargo End ================================================================":"", "Invoices Start ================================================================":"", diff --git a/assets/local/localization_mu.json b/assets/local/localization_mu.json index 5c16140..02666f6 100644 --- a/assets/local/localization_mu.json +++ b/assets/local/localization_mu.json @@ -351,7 +351,7 @@ "box.no_package":"There is no packages.", "box.input_cargo_weight":"Cargo type (lb)", "box.input_surcharge_item":"Surcharge items", - "box.select.cargo_type":"Select surcharge item", + "box.select.cargo_type":"Add surcharge item", "box.surcharge.item":"Surcharge items", "box.package.count":"Packages ({0})", "box.bill_to":"Bill to", @@ -370,6 +370,7 @@ "box.print.btn":"ပရင့်ထုတ်မည်", "box.delivery":"Delivery", "box.pickup":"Pick-up", + "box.add.cargo_type":"Add cargo type", "Boxes End ================================================================":"", "Delivery Start ================================================================":"", @@ -418,6 +419,7 @@ "FCSshipment.process.confirm":"လုပ်ငန်းစဉ်ကို အတည်ပြုပါ ?", "FCSshipment.arrive.confirm":"ရောက်ရှိကြောင်း အတည်ပြုပါ ?", "FCSshipment.invoice.confirm":"Invoice this shipment ?", + "FCSshipment.delete.confirm":"တင်ပို့ခြင်းကို ဖျက်မလား?", "FCS Shipment End ================================================================":"", "Shipment Start ================================================================":"", @@ -512,6 +514,8 @@ "cargo.qty":"အရေအတွက်", "cargo.amount":"ပမာဏ", "cargo.edit.delete.confirm":"ကုန်ပစ္စည်းကို ဖျက်မလား?", + "cargo.display_index":"Display index", + "cargo.is_default":"Default Cargo", "Cargo End ================================================================":"", "Invoices Start ================================================================":"", diff --git a/lib/constants.dart b/lib/constants.dart index 276f678..53f1ba7 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -9,6 +9,8 @@ const privilege_collection = "privileges"; const markets_collection = "markets"; const carton_sizes_collection = "carton_sizes"; const shipment_type_collection = "shipment_types"; +const shipment_consignee_collection = "shipment_consignees"; +const shipment_port_collection = "ports"; const packages_collection = "packages"; const messages_collection = "messages"; @@ -91,6 +93,7 @@ const shipment_courier_dropoff = "Courier drop off"; //Carton types const carton_from_packages = "For packages"; const carton_mix_carton = "For cartons (Mix carton)"; +const mix_carton = "Mix carton"; const carton_from_cartons = "From cartons"; const carton_from_shipments = "From shipments"; @@ -100,7 +103,7 @@ const carton_mix_box = "Mix box"; // carton Size const standardCarton = "Standard carton size"; const customCarton = "Custom size"; -const packageCartion = "Package"; +const packageCarton = "Package"; //Mix types const mix_delivery = "Mix Delivery"; @@ -140,9 +143,9 @@ const payment_confirmed_status = "confirmed"; const payment_canceled_status = "canceled"; //Delivery types -const delivery_caton = "Delivery"; -const pickup_carton = "Pick-up"; +const delivery_caton = "delivery"; +const pickup_carton = "pickup"; // bill -const billToSender = "Bill to sender"; -const billToConsignee = "Bill to consignee"; +const billToSender = "sender"; +const billToConsignee = "consignee"; diff --git a/lib/data/provider/carton_data_provider.dart b/lib/data/provider/carton_data_provider.dart index 3f519cc..c4de1cb 100644 --- a/lib/data/provider/carton_data_provider.dart +++ b/lib/data/provider/carton_data_provider.dart @@ -15,6 +15,7 @@ class CartonDataProvider { Future createCarton(Carton carton) async { var data = await requestAPI("/cartons", "POST", payload: carton.toMap(), token: await getToken()); + print("carton data:${data}"); return Carton.fromMap(data, data['id']); } @@ -25,7 +26,25 @@ class CartonDataProvider { Future deleteCarton(Carton carton) async { return await requestAPI("/cartons", "DELETE", - payload: carton.toMap(), token: await getToken()); + payload: {'id': carton.id}, token: await getToken()); + } + + Future createMixCarton(Carton carton) async { + var data = await requestAPI("/cartons", "POST", + payload: carton.toMapForMix(), token: await getToken()); + print("carton mix data:${data}"); + return Carton.fromMap(data, data['id']); + } + + Future updateMixCarton(Carton carton) async { + return await requestAPI("/cartons", "PUT", + payload: carton.toMapForMix(), token: await getToken()); + } + + Future uploadCartonImages(Carton carton) async { + return await requestAPI("/cartons/upload_images", "PUT", + payload: {'id': carton.id, "photo_urls": carton.photoUrls}, + token: await getToken()); } Future deliver(Carton carton) async { @@ -47,7 +66,7 @@ class CartonDataProvider { .collection(path) .where("carton_number", isEqualTo: term) .where("is_deleted", isEqualTo: false) - .orderBy("created_at", descending: true) + .orderBy("update_time", descending: true) .get(); return querySnap.docs.map((e) => Carton.fromMap(e.data(), e.id)).toList(); } catch (e) { diff --git a/lib/data/provider/rate_data_provider.dart b/lib/data/provider/rate_data_provider.dart index 9710469..bf60efa 100644 --- a/lib/data/provider/rate_data_provider.dart +++ b/lib/data/provider/rate_data_provider.dart @@ -38,6 +38,7 @@ class RateDataProvider { .doc(rate_doc_id) .collection(cargo_types_collection) .where("custom_duty", isEqualTo: false) + .orderBy("display_index", descending: false) .snapshots(); await for (var snaps in snapshots) { diff --git a/lib/data/services/carton_imp.dart b/lib/data/services/carton_imp.dart index 402f48e..04ebf5f 100644 --- a/lib/data/services/carton_imp.dart +++ b/lib/data/services/carton_imp.dart @@ -37,4 +37,19 @@ class CartonServiceImp implements CartonService { Future> searchCarton(String term) { return cartonDataProvider.searchCarton(term); } + + @override + Future createMixCarton(Carton carton) { + return cartonDataProvider.createMixCarton(carton); + } + + @override + Future updateMixCarton(Carton carton) { + return cartonDataProvider.updateMixCarton(carton); + } + + @override + Future uploadCartonImages(Carton carton) { + return cartonDataProvider.uploadCartonImages(carton); + } } diff --git a/lib/data/services/carton_service.dart b/lib/data/services/carton_service.dart index 1137373..ae2b6dd 100644 --- a/lib/data/services/carton_service.dart +++ b/lib/data/services/carton_service.dart @@ -6,4 +6,7 @@ abstract class CartonService { Future deleteCarton(Carton carton); Future deliver(Carton carton); Future> searchCarton(String term); + Future createMixCarton(Carton carton); + Future updateMixCarton(Carton carton); + Future uploadCartonImages(Carton carton); } diff --git a/lib/domain/entities/cargo_type.dart b/lib/domain/entities/cargo_type.dart index a4c0de7..b435209 100644 --- a/lib/domain/entities/cargo_type.dart +++ b/lib/domain/entities/cargo_type.dart @@ -9,6 +9,8 @@ class CargoType { double customDutyFee; double calRate; double calWeight; + int displayIndex; + bool isDefault; double get calAmount => calRate * calWeight; @@ -22,7 +24,9 @@ class CargoType { this.isChecked = false, this.qty = 0, this.isCutomDuty = false, - this.customDutyFee = 0}); + this.customDutyFee = 0, + this.displayIndex = 0, + this.isDefault = false}); factory CargoType.fromMap(Map map, String id) { return CargoType( @@ -33,8 +37,21 @@ class CargoType { calWeight: map['cal_weight']?.toDouble() ?? 0, calRate: map['cal_rate']?.toDouble() ?? 0, isCutomDuty: map['custom_duty'] ?? false, - customDutyFee: (map['custom_duty_fee'] ?? 0).toDouble()); - //qty: (map['qty'] ?? 0).toInt()); + customDutyFee: (map['custom_duty_fee'] ?? 0).toDouble(), + displayIndex: map['display_index'] ?? 0, + isDefault: map['is_defalut'] ?? false); + } + + factory CargoType.fromMapForCargo(Map map, String id) { + return CargoType( + id: id, name: map['name'], weight: map['weight']?.toDouble() ?? 0); + } + + factory CargoType.fromMapForsurcharge(Map map, String id) { + return CargoType( + id: id, + name: map['name'], + qty: map['qty'] == null ? 0 : int.tryParse(map['qty'].toString()) ?? 0); } Map toMap() { @@ -47,10 +64,20 @@ class CargoType { 'cal_rate': calRate, 'custom_duty': isCutomDuty, 'custom_duty_fee': customDutyFee, - 'qty': qty + 'qty': qty, + 'is_defalut': isDefault, + 'display_index': displayIndex }; } + Map toMapForCargo() { + return {"id": id, 'weight': weight}; + } + + Map toMapForSurcharge() { + return {"id": id, 'qty': qty}; + } + CargoType clone() { return CargoType.fromMap(toMap(), this.id!); } diff --git a/lib/domain/entities/carton.dart b/lib/domain/entities/carton.dart index 42ee221..9afef6e 100644 --- a/lib/domain/entities/carton.dart +++ b/lib/domain/entities/carton.dart @@ -10,33 +10,34 @@ import 'package.dart'; class Carton { String? id; - String? shipmentID; - String? shipmentNumber; + String? cartonNumber; + String? fcsShipmentID; + String? fcsShipmentNumber; + String? senderID; String? senderFCSID; String? senderName; - String? boxNumber; - String? status; - String? cargoDesc; - String? desc; + String? consigneeFCSID; + String? consigneeName; + String? consigneeID; + double width; double height; double length; + + String? status; + String? cargoDesc; + String? desc; + int? shipmentWeight; bool? isChecked; bool? isShipmentCarton; String? cartonType; - String? fcsID; - String? userName; - String? userID; - String? fcsShipmentID; - String? fcsShipmentNumber; - String? mixCartonID; + String? mixCartonNumber; - String? cartonSizeID; - String? cartonSizeName; - String? deliveryType; + + String? lastMile; String? cartonSizeType; double cartonWeight; @@ -50,19 +51,18 @@ class Carton { List photoUrls; String? remark; DateTime? arrivedDate; - String? cartonNumber; List packageIDs; List packages; List cargoTypes = []; + List surchareItems = []; DeliveryAddress? deliveryAddress; Shipment? shipment; - //for mix box - String? mixBoxType; - List mixCartons; - List mixCartonIDs; + //for mix carton + List cartons; + List cartonIDs; int get amount => (rate * weight); @@ -123,12 +123,9 @@ class Carton { Carton( {this.id, - this.shipmentID, - this.shipmentNumber, this.senderID, this.senderFCSID, this.senderName, - this.boxNumber, this.desc, this.width = 0, this.height = 0, @@ -136,9 +133,9 @@ class Carton { this.shipmentWeight, this.isChecked = false, this.cartonType, - this.fcsID, - this.userID, - this.userName, + this.consigneeFCSID, + this.consigneeID, + this.consigneeName, this.rate = 0, this.weight = 0, this.packageType, @@ -150,47 +147,60 @@ class Carton { this.shipmentHistory = const [], this.packages = const [], this.cargoTypes = const [], + this.surchareItems = const [], this.cartonNumber, this.billTo, this.fcsShipmentID, this.fcsShipmentNumber, this.packageIDs = const [], - this.mixCartonID, this.mixCartonNumber, this.isShipmentCarton = false, this.deliveryAddress, - this.cartonSizeID, - this.cartonSizeName, this.cartonSizeType, - this.deliveryType, - this.mixBoxType, - this.mixCartons = const [], - this.mixCartonIDs = const [], + this.lastMile, + this.cartons = const [], + this.cartonIDs = const [], this.cartonWeight = 0, this.photoUrls = const [], this.isSelected = false}); Map toMap() { - List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList(); - List _packages = packages.map((c) => c.toJson()).toList(); - List _mixCartons = mixCartons.map((c) => c.toJson()).toList(); + var _types = cargoTypes.where((t) => t.weight != 0).toList(); + var _cargoTypes = _types.map((c) => c.toMapForCargo()).toList(); + + var _packagesIds = packages.map((c) => c.id).toList(); + + var _surchareItems = + surchareItems.map((c) => c.toMapForSurcharge()).toList(); + return { 'id': id, + 'carton_type': cartonType, 'fcs_shipment_id': fcsShipmentID, - 'user_id': userID, - 'cargo_types': _cargoTypes, - 'packages': _packages, + 'sender_user_id': senderID, + 'consignee_user_id': consigneeID, + 'bill_to': billTo, + 'last_mile': lastMile, 'length': length, 'width': width, 'height': height, - 'delivery_address': deliveryAddress?.toMap(), + 'package_ids': _packagesIds, + 'cargo_types': _cargoTypes, + 'surcharge_items': _surchareItems, + }; + } + + Map toMapForMix() { + var _cartonIds = cartons.map((c) => c.id).toList(); + + return { + 'id': id, 'carton_type': cartonType, - 'mix_carton_id': mixCartonID, - 'mix_box_type': mixBoxType, - 'mix_cartons': _mixCartons, - 'sender_id': senderID, - 'sender_fcs_id': senderFCSID, - 'sender_name': senderName + 'fcs_shipment_id': fcsShipmentID, + 'length': length, + 'width': width, + 'height': height, + 'carton_ids': _cartonIds, }; } @@ -199,75 +209,48 @@ class Carton { map['arrived_date'] == null ? null : (map['arrived_date'] as Timestamp); var da = map['delivery_address']; var _da = da != null ? DeliveryAddress.fromMap(da, da["id"]) : null; + var cargoTypesMaps = List>.from(map['cargo_types'] ?? []); - var cargoTypes = - cargoTypesMaps.map((e) => CargoType.fromMap(e, e["id"])).toList(); - var mixCartonsMaps = - List>.from(map['mix_cartons'] ?? []); - var _mixCartons = - mixCartonsMaps.map((e) => Carton.fromMap(e, e["id"])).toList(); + var cargoTypes = cargoTypesMaps + .map((e) => CargoType.fromMapForCargo(e, e["id"])) + .toList(); + + var surchargeItemMaps = + List>.from(map['surcharge_items'] ?? []); + var surchageItems = surchargeItemMaps + .map((e) => CargoType.fromMapForsurcharge(e, e["id"])) + .toList(); + List _photoUrls = map['photo_urls'] == null ? [] : List.from(map['photo_urls']); return Carton( - id: docID, - arrivedDate: _arrivedDate != null ? _arrivedDate.toDate() : null, - shipmentID: map['shipment_id'], - shipmentNumber: map['shipment_number'], - // receiverNumber: map['receiver_number'], - boxNumber: map['box_number'], - length: double.tryParse(map['length'].toString()) ?? 0, - width: double.tryParse(map['width'].toString()) ?? 0, - height: double.tryParse(map['height'].toString()) ?? 0, - userName: map['user_name'], - fcsID: map['fcs_id'], - cartonType: map['carton_type'], - cartonNumber: map['carton_number'], - userID: map['user_id'], - fcsShipmentID: map['fcs_shipment_id'], - fcsShipmentNumber: map['fcs_shipment_number'], - isShipmentCarton: map['is_shipment_carton'], - mixCartonID: map['mix_carton_id'], - mixCartonNumber: map['mix_carton_number'], - status: map['status'], - packageIDs: List.from(map['package_ids'] ?? []), - deliveryAddress: _da, - cargoTypes: cargoTypes, - mixBoxType: map['mix_box_type'], - mixCartons: _mixCartons, - senderID: map['sender_id'], - senderFCSID: map['sender_fcs_id'], - senderName: map['sender_name'], - mixCartonIDs: List.from(map['mix_carton_ids'] ?? []), - cartonWeight: (map['carton_weight'] ?? 0).toDouble(), - photoUrls: _photoUrls, - ); - } - - Map toJson() { - List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList(); - List _packages = packages.map((c) => c.toJson()).toList(); - List _mixCartons = mixCartons.map((c) => c.toJson()).toList(); - return { - 'id': id, - 'fcs_shipment_id': fcsShipmentID, - 'user_id': userID, - 'cargo_types': _cargoTypes, - 'packages': _packages, - 'length': length, - 'width': width, - 'height': height, - 'delivery_address': deliveryAddress?.toMap(), - 'carton_type': cartonType, - 'mix_carton_id': mixCartonID, - 'mix_box_type': mixBoxType, - 'mix_cartons': _mixCartons, - 'sender_id': senderID, - 'sender_fcs_id': senderFCSID, - 'sender_name': senderName, - "photo_urls": photoUrls - }; + id: docID, + arrivedDate: _arrivedDate != null ? _arrivedDate.toDate() : null, + length: double.tryParse(map['length'].toString()) ?? 0, + width: double.tryParse(map['width'].toString()) ?? 0, + height: double.tryParse(map['height'].toString()) ?? 0, + cartonType: map['carton_type'], + cartonNumber: map['carton_number'], + fcsShipmentID: map['fcs_shipment_id'], + fcsShipmentNumber: map['fcs_shipment_number'], + status: map['status'], + packageIDs: List.from(map['package_ids'] ?? []), + deliveryAddress: _da, + cargoTypes: cargoTypes, + surchareItems: surchageItems, + senderID: map['sender_user_id'], + senderFCSID: map['sender_fcs_id'], + senderName: map['sender_user_name'], + consigneeID: map['consignee_user_id'], + consigneeName: map['consignee_user_name'], + consigneeFCSID: map['consignee_fcs_id'], + cartonIDs: List.from(map['carton_ids'] ?? []), + cartonWeight: (map['carton_weight'] ?? 0).toDouble(), + photoUrls: _photoUrls, + billTo: map['bill_to'] ?? '', + lastMile: map['last_mile'] ?? ""); } @override diff --git a/lib/domain/entities/fcs_shipment.dart b/lib/domain/entities/fcs_shipment.dart index 13ceb57..5e6c604 100644 --- a/lib/domain/entities/fcs_shipment.dart +++ b/lib/domain/entities/fcs_shipment.dart @@ -4,13 +4,16 @@ class FcsShipment { String? id; String? shipmentNumber; DateTime? cutoffDate; + DateTime? etaDate; String? shipmentTypeId; - String? shipTypeName; - DateTime? arrivalDate; + String? shipmentTypeName; DateTime? departureDate; - String? consignee; - String? port; - String? destination; + String? consigneeId; + String? consigneeName; + String? loadingPortId; + String? loadingPortName; + String? destinationPortId; + String? destinationPortName; String? status; String? reportName; @@ -19,13 +22,16 @@ class FcsShipment { this.shipmentNumber, this.cutoffDate, this.shipmentTypeId, - this.shipTypeName, + this.shipmentTypeName, this.status, - this.arrivalDate, + this.etaDate, this.departureDate, - this.consignee, - this.port, - this.destination, + this.consigneeId, + this.consigneeName, + this.loadingPortId, + this.loadingPortName, + this.destinationPortId, + this.destinationPortName, this.reportName, }); @@ -33,45 +39,45 @@ class FcsShipment { var _cutoffDate = map['cutoff_date'] == null ? null : (map['cutoff_date'] as Timestamp); var _arrivalDate = - map['arrival_date'] == null ? null : (map['arrival_date'] as Timestamp); + map['eta_date'] == null ? null : (map['eta_date'] as Timestamp); return FcsShipment( - id: docID, - cutoffDate: _cutoffDate != null ? _cutoffDate.toDate() : null, - arrivalDate: _arrivalDate != null ? _arrivalDate.toDate() : null, - shipmentNumber: map['shipment_number'], - shipTypeName: map['shipment_type_name'], - shipmentTypeId: map['shipment_type_id'] ?? "", - status: map['status'], - consignee: map['consignee'], - port: map['port'], - destination: map['destination'], - ); + id: docID, + cutoffDate: _cutoffDate != null ? _cutoffDate.toDate() : null, + etaDate: _arrivalDate != null ? _arrivalDate.toDate() : null, + shipmentNumber: map['shipment_number'], + shipmentTypeId: map['shipment_type_id'] ?? "", + shipmentTypeName: map['shipment_type_name'], + status: map['status'], + consigneeId: map['shipment_consignee_id'], + consigneeName: map['shipment_consignee_name'], + loadingPortId: map['loading_port_id'], + loadingPortName: map['loading_port_name'], + destinationPortId: map['destination_port_id'], + destinationPortName: map['destination_port_name']); } Map toMap() { return { 'id': id, 'shipment_number': shipmentNumber, - 'shipment_type_id': shipmentTypeId, 'cutoff_date': cutoffDate?.toUtc().toIso8601String(), - 'arrival_date': arrivalDate?.toUtc().toIso8601String(), - 'consignee': consignee, - 'port': port, - 'destination': destination, - // 'status': status, - // 'report_name': reportName, + 'eta_date': etaDate?.toUtc().toIso8601String(), + 'shipment_type_id': shipmentTypeId, + 'shipment_consignee_id': consigneeId, + 'loading_port_id': loadingPortId, + 'destination_port_id': destinationPortId }; } bool isChangedForEdit(FcsShipment fcsShipment) { return fcsShipment.shipmentNumber != this.shipmentNumber || fcsShipment.cutoffDate != this.cutoffDate || - fcsShipment.arrivalDate != this.arrivalDate || + fcsShipment.etaDate != this.etaDate || fcsShipment.shipmentTypeId != this.shipmentTypeId || - fcsShipment.consignee != this.consignee || - fcsShipment.port != this.port || - fcsShipment.destination != this.destination; + fcsShipment.consigneeId != this.consigneeId || + fcsShipment.loadingPortId != this.loadingPortId || + fcsShipment.destinationPortId != this.destinationPortId; } @override diff --git a/lib/domain/entities/shipment_consignee.dart b/lib/domain/entities/shipment_consignee.dart new file mode 100644 index 0000000..efa522f --- /dev/null +++ b/lib/domain/entities/shipment_consignee.dart @@ -0,0 +1,17 @@ +class ShipmentConsignee { + String id; + String name; + + ShipmentConsignee({required this.id, required this.name}); + + factory ShipmentConsignee.fromMap(Map map, String id) { + return ShipmentConsignee(id: id, name: map['name'] ?? ""); + } + + @override + bool operator ==(Object other) => + other is ShipmentConsignee && other.id == id; + + @override + int get hashCode => id.hashCode; +} diff --git a/lib/domain/entities/shipment_port.dart b/lib/domain/entities/shipment_port.dart new file mode 100644 index 0000000..720060e --- /dev/null +++ b/lib/domain/entities/shipment_port.dart @@ -0,0 +1,17 @@ +class ShipmentPort { + String id; + String name; + + ShipmentPort({required this.id, required this.name}); + + factory ShipmentPort.fromMap(Map map, String id) { + return ShipmentPort(id: id, name: map['name'] ?? ""); + } + + @override + bool operator ==(Object other) => + other is ShipmentPort && other.id == id; + + @override + int get hashCode => id.hashCode; +} diff --git a/lib/domain/entities/shipment_type.dart b/lib/domain/entities/shipment_type.dart index d104435..ec66f32 100644 --- a/lib/domain/entities/shipment_type.dart +++ b/lib/domain/entities/shipment_type.dart @@ -7,4 +7,10 @@ class ShipmentType { factory ShipmentType.fromMap(Map map, String id) { return ShipmentType(id: id, name: map['name'] ?? ""); } + + @override + bool operator ==(Object other) => other is ShipmentType && other.id == id; + + @override + int get hashCode => id.hashCode; } diff --git a/lib/domain/vo/local_popupmenu.dart b/lib/domain/vo/local_popupmenu.dart index 79fe34e..482793e 100644 --- a/lib/domain/vo/local_popupmenu.dart +++ b/lib/domain/vo/local_popupmenu.dart @@ -16,7 +16,7 @@ class LocalPopupMenu { List shipFiteringMenu = [ LocalPopupMenu(id: 0, text :"All"), LocalPopupMenu(id: 1, text: "Pending"), - LocalPopupMenu(id: 2, text: "Processing"), + LocalPopupMenu(id: 2, text: "Processed"), LocalPopupMenu(id: 3, text: "Shipped"), LocalPopupMenu(id: 4, text: "Arrived"), LocalPopupMenu(id: 5, text: "Invoiced"), diff --git a/lib/helpers/api_helper.dart b/lib/helpers/api_helper.dart index 6839e45..240a9df 100644 --- a/lib/helpers/api_helper.dart +++ b/lib/helpers/api_helper.dart @@ -51,7 +51,7 @@ Future requestAPI(String path, method, }); String curlPath = - "curl -X $method ${list.join("")}${payload != null ? '-d $payload ' : ''}${options.baseUrl}$path"; + "curl -X $method ${list.join("")}${payload != null ? '-d \'${jsonEncode(payload)}\' ' : ''}${options.baseUrl}$path"; log.log(curlPath); diff --git a/lib/helpers/pdf.dart b/lib/helpers/pdf.dart index a80784c..97f05c4 100644 --- a/lib/helpers/pdf.dart +++ b/lib/helpers/pdf.dart @@ -48,7 +48,7 @@ Future generateCartonPdf(Carton carton) async { style: pw.TextStyle( fontSize: 12, )), - pw.Text(carton.userName!, + pw.Text(carton.consigneeName!, style: pw.TextStyle( fontSize: 10, )), diff --git a/lib/main-dev.dart b/lib/main-dev.dart index 2ad3377..850c75a 100644 --- a/lib/main-dev.dart +++ b/lib/main-dev.dart @@ -19,7 +19,8 @@ Future main() async { Config( flavor: Flavor.DEV, color: Colors.blue, - apiURL: "https://asia-northeast1-fcs-dev1.cloudfunctions.net/API13", + apiURL:"http://192.168.100.150:9090", + // apiURL: "https://asia-northeast1-fcs-dev1.cloudfunctions.net/API13", reportURL: "http://petrok.mokkon.com:7071", reportProjectID: "fcs-dev", bucketName: "gs://fcs-dev1-us", diff --git a/lib/pages/carton/cargo_type_addition.dart b/lib/pages/carton/cargo_type_addition.dart new file mode 100644 index 0000000..9216c91 --- /dev/null +++ b/lib/pages/carton/cargo_type_addition.dart @@ -0,0 +1,114 @@ +import 'package:fcs/domain/entities/cargo_type.dart'; +import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/rates/model/shipment_rate_model.dart'; +import 'package:fcs/pages/widgets/local_app_bar.dart'; +import 'package:fcs/pages/widgets/progress.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../main/util.dart'; + +class CargoTypeAddition extends StatefulWidget { + final List cargoTypes; + const CargoTypeAddition({super.key, required this.cargoTypes}); + @override + _CargoTypeAdditionState createState() => _CargoTypeAdditionState(); +} + +class _CargoTypeAdditionState extends State { + bool _isLoading = false; + List cargoTypes = []; + + @override + void initState() { + _init(); + super.initState(); + } + + _init() { + var shipmentRateModel = + Provider.of(context, listen: false); + cargoTypes = + shipmentRateModel.rate.cargoTypes.map((e) => e.clone()).toList(); + + for (var p in cargoTypes) { + p.qty = 0; + if (widget.cargoTypes.any((e) => e.id == p.id)) { + p.isChecked = true; + } else { + p.isChecked = false; + } + + widget.cargoTypes.forEach((vp) { + if (p.id == vp.id) { + p.qty = vp.qty; + } + }); + } + + if (mounted) { + setState(() {}); + } + } + + _onTap(CargoType cargo) { + if (cargo.isChecked) { + showMsgDialog(context, "Error", "Already exit!"); + return; + } + Navigator.pop(context, cargo); + } + + @override + Widget build(BuildContext context) { + List getCargoRowList(List _c) { + return _c.map((c) { + return ListTile( + onTap: () { + _onTap(c); + }, + title: new Text(c.name ?? '', style: textStyle), + trailing: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: primaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + minimumSize: Size(80, 35)), + onPressed: c.isChecked + ? null + : () { + _onTap(c); + }, + child: Text( + "Add", + style: TextStyle(color: Colors.white, fontSize: 14), + ), + )); + }).toList(); + } + + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: LocalAppBar( + labelKey: 'box.add.cargo_type', + backgroundColor: Colors.white, + labelColor: primaryColor, + arrowColor: primaryColor), + body: Container( + padding: EdgeInsets.all(10), + child: ListView( + shrinkWrap: true, + children: [ + Column( + children: getCargoRowList(cargoTypes), + ), + SizedBox(height: 30), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/carton/cargo_widget.dart b/lib/pages/carton/cargo_widget.dart index dde9a67..9b5b021 100644 --- a/lib/pages/carton/cargo_widget.dart +++ b/lib/pages/carton/cargo_widget.dart @@ -12,7 +12,8 @@ import '../widgets/continue_button.dart'; import '../widgets/display_text.dart'; import '../widgets/local_title.dart'; import '../widgets/previous_button.dart'; -import 'custom_duty_addition.dart'; +import 'cargo_type_addition.dart'; +import 'surcharge_item_addition.dart'; typedef OnPrevious = Function( List cargoTypes, List customDuties); @@ -56,28 +57,20 @@ class _CargoWidgetState extends State { _init() { // for cargo types - var model = context.read(); - _cargoTypes = model.rate.cargoTypes.map((e) => e.clone()).toList(); - if (widget.cargoTypes.isNotEmpty) { - _cargoTypes.forEach((mp) { - mp.weight = 0; - widget.cargoTypes.forEach((vp) { - if (mp.id == vp.id) { - mp.weight = vp.weight; - } - }); - }); + _cargoTypes = List.from(widget.cargoTypes); _cargoTypes.forEach((e) { var editor = new TextEditingController(); editor.text = removeTrailingZeros(e.weight); editor.addListener(inputChangeListener); _cargoTypeControllers.add(editor); }); - - double total = _cargoTypes.fold(0, (sum, value) => sum + value.weight); - _totalCtl.text = removeTrailingZeros(total); + _calculateTotalWeght(); } else { + var model = context.read(); + var cargoes = model.rate.cargoTypes.map((e) => e.clone()).toList(); + + _cargoTypes = cargoes.where((e) => e.isDefault).toList(); _cargoTypes.forEach((e) { var editor = new TextEditingController(); editor.text = ''; @@ -103,6 +96,11 @@ class _CargoWidgetState extends State { } } + _calculateTotalWeght() { + double total = _cargoTypes.fold(0, (sum, value) => sum + value.weight); + _totalCtl.text = removeTrailingZeros(total); + } + bool isFieldEmpty(int index) { return _cargoTypeControllers[index].text.isEmpty; } @@ -158,6 +156,39 @@ class _CargoWidgetState extends State { ], ); + final cargoTitle = LocalTitle( + textKey: "box.input_cargo_weight", + topPadding: 0, + trailing: IconButton( + icon: Icon( + Icons.add_circle, + color: primaryColor, + ), + onPressed: () async { + CargoType? cargoType = await Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => + CargoTypeAddition(cargoTypes: _cargoTypes))); + if (cargoType == null) return; + _cargoTypes.add(cargoType); + + _cargoTypeControllers.clear(); + + _cargoTypes.forEach((e) { + var editor = new TextEditingController(); + editor.text = removeTrailingZeros(e.weight); + editor.addListener(inputChangeListener); + _cargoTypeControllers.add(editor); + }); + + _calculateTotalWeght(); + if (mounted) { + setState(() {}); + } + }), + ); + final cargosBox = Wrap( alignment: WrapAlignment.spaceBetween, runSpacing: 15, @@ -171,6 +202,8 @@ class _CargoWidgetState extends State { InkResponse( radius: 25, onTap: () { + _cargoTypes.removeAt(key); + double totalWeight = double.tryParse(_totalCtl.text) ?? 0; double removeWeight = (double.tryParse(_cargoTypeControllers[key].text) ?? @@ -182,11 +215,12 @@ class _CargoWidgetState extends State { } _cargoTypeControllers[key].clear(); + if (mounted) { setState(() {}); } }, - child: Icon(MaterialIcons.clear, color: labelColor)), + child: Icon(Feather.minus_circle, color: labelColor)), const SizedBox(width: 10), Flexible( child: inputTextFieldWidget(context, @@ -254,14 +288,14 @@ class _CargoWidgetState extends State { color: primaryColor, ), onPressed: () async { - List? customList = await Navigator.push>( + CargoType? surchargeItem = await Navigator.push( context, CupertinoPageRoute( builder: (context) => - CustomDutyAddition(customDuties: _surchareItems))); - if (customList == null) return; + SurchargeItemAddition(items: _surchareItems))); + if (surchargeItem == null) return; - _surchareItems = List.from(customList); + _surchareItems.add(surchargeItem); _surchargeControllers.clear(); _surchareItems.asMap().entries.forEach((e) { @@ -315,10 +349,11 @@ class _CargoWidgetState extends State { final continueBtn = ContinueButton( onTap: () { if (widget.onContinue != null) { - - if(_surchareItems.isNotEmpty && _surchareItems.any((item)=> item.qty == 0)){ - showMsgDialog(context, "Error", "Please insert surcharge item quantity"); - return; + if (_surchareItems.isNotEmpty && + _surchareItems.any((item) => item.qty == 0)) { + showMsgDialog( + context, "Error", "Please insert surcharge item quantity"); + return; } widget.onContinue!(_cargoTypes, _surchareItems); @@ -341,7 +376,7 @@ class _CargoWidgetState extends State { children: [ const SizedBox(height: 8), userRow, - LocalTitle(textKey: "box.input_cargo_weight", topPadding: 5), + cargoTitle, cargosBox, const SizedBox(height: 15), Divider(), diff --git a/lib/pages/carton/carton_editor.dart b/lib/pages/carton/carton_editor.dart index 315a655..334f811 100644 --- a/lib/pages/carton/carton_editor.dart +++ b/lib/pages/carton/carton_editor.dart @@ -56,7 +56,9 @@ class _CartonEditorState extends State { _isNew = false; _consignee = User( - id: _carton!.userID, fcsID: _carton!.fcsID, name: _carton!.userName); + id: _carton!.consigneeID, + fcsID: _carton!.consigneeFCSID, + name: _carton!.consigneeName); _sender = User( id: _carton!.senderID, fcsID: _carton!.senderFCSID, @@ -129,7 +131,7 @@ class _CartonEditorState extends State { padding: const EdgeInsets.only(right: 5), child: InkResponse( radius: 30, - onTap: () { + onTap: () async { //for packages if (isFromPackages) { if (_sender == null) { @@ -149,20 +151,32 @@ class _CartonEditorState extends State { return; } - Navigator.push( + Carton? carton = await Navigator.push( context, CupertinoPageRoute( builder: (context) => CartonPackageForm( sender: _sender!, consignee: _consignee!, ))); + + if (carton != null) { + setState(() { + _cartons.add(carton); + }); + } } // for mix cartion else { - Navigator.push( + Carton? carton = await Navigator.push( context, CupertinoPageRoute( builder: (context) => const MixCartonForm())); + + if (carton != null) { + setState(() { + _cartons.add(carton); + }); + } } }, child: Icon(Icons.add_circle, color: primaryColor), diff --git a/lib/pages/carton/carton_info.dart b/lib/pages/carton/carton_info.dart index 9cc2157..be1fec1 100644 --- a/lib/pages/carton/carton_info.dart +++ b/lib/pages/carton/carton_info.dart @@ -18,6 +18,7 @@ import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import '../../domain/entities/carton_size.dart'; +import '../carton_size/model/carton_size_model.dart'; import '../widgets/local_button.dart'; import 'carton_package_editor.dart'; import 'mix_carton/mix_carton_editor.dart'; @@ -56,27 +57,41 @@ class _CartonInfoState extends State { _init() async { try { _isLoading = true; - _carton.cartonType = carton_from_packages; - _carton.billTo = billToConsignee; - _carton.cartonSizeType = customCarton; _multiImgController.setImageUrls = _carton.photoUrls; - _cargoTypes = _carton.cargoTypes.where((e) => !e.isCutomDuty).toList(); - _surchareItems = _carton.cargoTypes.where((e) => e.isCutomDuty).toList(); + _cargoTypes = _carton.cargoTypes; + _surchareItems = _carton.surchareItems; + + // check carton size type + List cartonSizes = + context.read().cartonSizes; + var sameLength = cartonSizes.any((size) => size.length == _carton.length); + var sameWidth = cartonSizes.any((size) => size.width == _carton.width); + var sameHeight = cartonSizes.any((size) => size.height == _carton.height); + bool isStandartSize = sameLength && sameWidth && sameHeight; + if (isStandartSize) { + _carton.cartonSizeType = standardCarton; + standardSize = cartonSizes.firstWhere((size) => + size.length == _carton.length && + size.width == _carton.width && + size.height == _carton.height); + } else if (_carton.length == 0 && + _carton.width == 0 && + _carton.height == 0) { + _carton.cartonSizeType = packageCarton; + } else { + _carton.cartonSizeType = customCarton; + } + if (_carton.cartonType == carton_from_packages) { - _carton.deliveryType = delivery_caton; _packages = await context .read() .getPackagesByIds(_carton.packageIDs); } - if (_carton.cartonType == carton_mix_carton) { + if (_carton.cartonType == mix_carton) { _mixCartons = await context .read() - .getCartonsByIds(_carton.mixCartonIDs); - } - - if (mounted) { - setState(() {}); + .getCartonsByIds(_carton.cartonIDs); } totalWeight = @@ -86,17 +101,23 @@ class _CartonInfoState extends State { } finally { _isLoading = false; } + + if (mounted) { + setState(() {}); + } } @override Widget build(BuildContext context) { - String? boxDimension = _carton.cartonSizeType == standardCarton + var fromPackage = _carton.cartonType == carton_from_packages; + + String? boxDimension = _carton.cartonSizeType == standardCarton ? "${standardSize?.name} - ${standardSize?.length.toInt()}”x${standardSize?.width.toInt()}”x${standardSize?.height.toInt()}”" : _carton.cartonSizeType == customCarton ? "${_carton.length.toInt()}”x${_carton.width.toInt()}”x${_carton.height.toInt()}”" : null; - final cartonTypeBox = DisplayText( + final cartonNumberBox = DisplayText( text: _carton.cartonNumber, labelTextKey: "box.number", ); @@ -111,10 +132,12 @@ class _CartonInfoState extends State { }, icon: Icon(AntDesign.qrcode, color: Colors.black)); - final cartonSubTypeBox = DisplayText( + final cartonTypeBox = DisplayText( text: _carton.cartonType == carton_from_packages ? "For packages" - : "Mix carton", + : _carton.cartonType == mix_carton + ? carton_mix_carton + : '', labelTextKey: "box.carton.type", ); @@ -132,13 +155,13 @@ class _CartonInfoState extends State { labelTextKey: "box.fcs_shipment_num", ); - final deliveryBox = DisplayText( - text: _carton.deliveryType, + final lastMileBox = DisplayText( + text: _carton.lastMile == delivery_caton ? 'Delivery' : 'Pick-up', labelTextKey: "box.delivery_type", ); final cartonSizeBox = DisplayText( - subText: boxDimension == null ? null : Text("$boxDimension"), + subText: Text("${boxDimension ?? 'No defined size'}"), labelTextKey: "box.carton_size", ); @@ -149,8 +172,8 @@ class _CartonInfoState extends State { ); final consigneeNameBox = DisplayText( - text: _carton.userName != null ? _carton.userName : "", - subText: Text(_carton.fcsID ?? "", style: textStyle), + text: _carton.consigneeName != null ? _carton.consigneeName : "", + subText: Text(_carton.consigneeFCSID ?? "", style: textStyle), labelTextKey: "processing.consignee.name", ); @@ -367,30 +390,29 @@ class _CartonInfoState extends State { padding: const EdgeInsets.only(left: 20, right: 20), child: ListView(children: [ Row(children: [ - Flexible(child: cartonTypeBox,flex: 1), + Flexible(child: cartonNumberBox, flex: 1), Flexible( child: cartonQrBox, ), - ]), Row( children: [ - Flexible(child: cartonSubTypeBox), + Flexible(child: cartonTypeBox), ], ), - _mixCartons.isEmpty ? userRowBox : const SizedBox(), + fromPackage ? userRowBox : const SizedBox(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Flexible(child: billInfoBox), + fromPackage ? Flexible(child: billInfoBox) : SizedBox(), Flexible(child: shipmentBox), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - _mixCartons.isEmpty - ? Flexible(child: deliveryBox) + fromPackage + ? Flexible(child: lastMileBox) : const SizedBox(), Flexible(child: cartonSizeBox), ], @@ -430,7 +452,9 @@ class _CartonInfoState extends State { uploadImageBtn, const SizedBox(height: 30), img, - const SizedBox(height: 40), + _carton.photoUrls.isNotEmpty + ? const SizedBox(height: 40) + : const SizedBox(), deleteBtn, const SizedBox(height: 20) ])))); @@ -457,17 +481,22 @@ class _CartonInfoState extends State { } _gotoEditor() async { - bool? updated = _carton.cartonType == carton_mix_carton - ? await Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => MixCartonEditor(carton: _carton)), - ) - : await Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => CartonPackageEditor(carton: _carton)), - ); + bool? updated = false; + if (_carton.cartonType == mix_carton) { + updated = await Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => MixCartonEditor(carton: _carton)), + ); + } + + if (_carton.cartonType == carton_from_packages) { + updated = await Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => CartonPackageEditor(carton: _carton)), + ); + } if (updated ?? false) { Carton? c = @@ -489,9 +518,8 @@ class _CartonInfoState extends State { _isLoading = true; }); try { - // var cartonModel = Provider.of(context, listen: false); - // await cartonModel.deleteCarton(widget.carton); - // Navigator.pop(context, true); + await context.read().deleteCarton(widget.carton); + Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); } finally { diff --git a/lib/pages/carton/carton_package_editor.dart b/lib/pages/carton/carton_package_editor.dart index 488ff97..5fe0ca5 100644 --- a/lib/pages/carton/carton_package_editor.dart +++ b/lib/pages/carton/carton_package_editor.dart @@ -1,5 +1,6 @@ // ignore_for_file: deprecated_member_use +import 'package:fcs/pages/carton/model/carton_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; @@ -14,6 +15,7 @@ import '../../domain/entities/cargo_type.dart'; import '../../domain/entities/carton.dart'; import '../../domain/entities/package.dart'; import '../../domain/entities/user.dart'; +import '../carton_size/model/carton_size_model.dart'; import '../fcs_shipment/model/fcs_shipment_model.dart'; import '../main/util.dart'; import '../package/model/package_model.dart'; @@ -53,7 +55,7 @@ class _CartonPackageEditorState extends State { double _width = 0; double _height = 0; - String _selectedDeliveryType = delivery_caton; + String _selectedLastMile = delivery_caton; String _billToValue = billToSender; FcsShipment? _shipment; @@ -78,20 +80,14 @@ class _CartonPackageEditorState extends State { id: widget.carton.senderID); _consignee = User( - id: widget.carton.userID, - name: widget.carton.userName, - fcsID: widget.carton.fcsID); + id: widget.carton.consigneeID, + name: widget.carton.consigneeName, + fcsID: widget.carton.consigneeFCSID); _billToValue = widget.carton.billTo ?? billToSender; - _selectedDeliveryType = widget.carton.deliveryType ?? delivery_caton; - _cartonSizeType = widget.carton.cartonSizeType ?? customCarton; - _length = widget.carton.length; - _width = widget.carton.width; - _height = widget.carton.height; - _cargoTypes = - widget.carton.cargoTypes.where((e) => !e.isCutomDuty).toList(); - _surchareItems = - widget.carton.cargoTypes.where((e) => e.isCutomDuty).toList(); + _selectedLastMile = widget.carton.lastMile ?? delivery_caton; + _cargoTypes = widget.carton.cargoTypes; + _surchareItems = widget.carton.surchareItems; var s = await context .read() @@ -102,6 +98,30 @@ class _CartonPackageEditorState extends State { .read() .getPackagesByIds(widget.carton.packageIDs); + // check carton size type + List cartonSizes = context.read().cartonSizes; + + var sameLength = + cartonSizes.any((size) => size.length == widget.carton.length); + var sameWidth = + cartonSizes.any((size) => size.width == widget.carton.width); + var sameHeight = + cartonSizes.any((size) => size.height == widget.carton.height); + bool isStandartSize = sameLength && sameWidth && sameHeight; + if (isStandartSize) { + _cartonSizeType = standardCarton; + _standardSize = cartonSizes.firstWhere((size) => + size.length == widget.carton.length && + size.width == widget.carton.width && + size.height == widget.carton.height); + } else if (widget.carton.length == 0 && + widget.carton.width == 0 && + widget.carton.height == 0) { + _cartonSizeType = packageCarton; + } else { + _cartonSizeType = customCarton; + } + if (mounted) { setState(() {}); } @@ -143,8 +163,16 @@ class _CartonPackageEditorState extends State { }, ), backgroundColor: Colors.white, - title: LocalText(context, 'box.update_title', - color: primaryColor, fontSize: 20), + title: Column( + children: [ + LocalText(context, 'box.update_title', + color: primaryColor, fontSize: 20), + Text( + widget.carton.cartonNumber ?? '', + style: TextStyle(color: primaryColor, fontSize: 14), + ) + ], + ), ), body: Column( children: [ @@ -172,7 +200,7 @@ class _CartonPackageEditorState extends State { if (step.stepType == StepType.SIZE) { return Expanded( child: CartonSizeWidget( - deliveryType: _selectedDeliveryType, + lastMile: _selectedLastMile, billType: _billToValue, sender: _sender!, consignee: _consignee!, @@ -188,7 +216,7 @@ class _CartonPackageEditorState extends State { onContinue: (deliveryType, billType, shipment, cartonSizeType, {standardSize, length, width, height}) { setState(() { - _selectedDeliveryType = deliveryType; + _selectedLastMile = deliveryType; _billToValue = billType; _shipment = shipment; _cartonSizeType = cartonSizeType; @@ -256,7 +284,7 @@ class _CartonPackageEditorState extends State { length: _length, width: _width, height: _height, - deliveryType: _selectedDeliveryType, + lastMile: _selectedLastMile, shipment: _shipment!, packages: _packages, cargoTypes: _cargoTypes, @@ -279,6 +307,40 @@ class _CartonPackageEditorState extends State { _isLoading = true; }); try { + double length = 0; + double width = 0; + double height = 0; + + if (_cartonSizeType == standardCarton) { + if (_standardSize != null) { + length = _standardSize!.length; + width = _standardSize!.width; + height = _standardSize!.height; + } + } else if (_cartonSizeType == customCarton) { + length = _length; + width = _width; + height = _height; + } else { + length = 0; + width = 0; + height = 0; + } + + var carton = Carton( + cartonType: carton_from_packages, + billTo: _billToValue, + lastMile: _selectedLastMile, + fcsShipmentID: _shipment?.id, + length: length, + width: width, + height: height, + packages: _packages, + cargoTypes: _cargoTypes, + surchareItems: _surchareItems); + + await context.read().updateCarton(carton); + Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); diff --git a/lib/pages/carton/carton_package_form.dart b/lib/pages/carton/carton_package_form.dart index ddd9cac..f031418 100644 --- a/lib/pages/carton/carton_package_form.dart +++ b/lib/pages/carton/carton_package_form.dart @@ -1,5 +1,6 @@ // ignore_for_file: deprecated_member_use +import 'package:fcs/domain/entities/carton.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; @@ -20,6 +21,7 @@ import '../widgets/step_widget.dart'; import 'cargo_widget.dart'; import 'carton_size_widget.dart'; import 'carton_submit.dart'; +import 'model/carton_model.dart'; import 'model/package_selection_model.dart'; import 'package_selection_widget.dart'; @@ -55,7 +57,7 @@ class _CartonPackageFormState extends State { double _width = 0; double _height = 0; - String _selectedDeliveryType = delivery_caton; + String _selectedLastMile = delivery_caton; String _billToValue = billToSender; FcsShipment? _shipment; @@ -134,7 +136,7 @@ class _CartonPackageFormState extends State { if (step.stepType == StepType.SIZE) { return Expanded( child: CartonSizeWidget( - deliveryType: _selectedDeliveryType, + lastMile: _selectedLastMile, billType: _billToValue, sender: widget.sender, consignee: widget.consignee, @@ -147,10 +149,10 @@ class _CartonPackageFormState extends State { onPrevious: () { Navigator.pop(context); }, - onContinue: (deliveryType, billType, shipment, cartonSizeType, + onContinue: (lastMile, billType, shipment, cartonSizeType, {standardSize, length, width, height}) { setState(() { - _selectedDeliveryType = deliveryType; + _selectedLastMile = lastMile; _billToValue = billType; _shipment = shipment; _cartonSizeType = cartonSizeType; @@ -217,7 +219,7 @@ class _CartonPackageFormState extends State { length: _length, width: _width, height: _height, - deliveryType: _selectedDeliveryType, + lastMile: _selectedLastMile, shipment: _shipment!, packages: _packages, cargoTypes: _cargoTypes, @@ -240,7 +242,41 @@ class _CartonPackageFormState extends State { _isLoading = true; }); try { - Navigator.pop(context, true); + double length = 0; + double width = 0; + double height = 0; + + if (_cartonSizeType == standardCarton) { + if (_standardSize != null) { + length = _standardSize!.length; + width = _standardSize!.width; + height = _standardSize!.height; + } + } else if (_cartonSizeType == customCarton) { + length = _length; + width = _width; + height = _height; + } else { + length = 0; + width = 0; + height = 0; + } + + var carton = Carton( + cartonType: carton_from_packages, + senderID: widget.sender.id, + consigneeID: widget.consignee.id, + billTo: _billToValue, + lastMile: _selectedLastMile, + fcsShipmentID: _shipment?.id, + length: length, + width: width, + height: height, + packages: _packages, + cargoTypes: _cargoTypes, + surchareItems: _surchareItems); + var c = await context.read().createCarton(carton); + Navigator.pop(context, c); } catch (e) { showMsgDialog(context, "Error", e.toString()); } finally { diff --git a/lib/pages/carton/carton_size_widget.dart b/lib/pages/carton/carton_size_widget.dart index b23a6ab..9beaa4b 100644 --- a/lib/pages/carton/carton_size_widget.dart +++ b/lib/pages/carton/carton_size_widget.dart @@ -31,7 +31,7 @@ class CartonSizeWidget extends StatefulWidget { final OnContinue? onContinue; final User sender; final User consignee; - final String deliveryType; + final String lastMile; final String billType; final FcsShipment? shipment; final String cartonSizeType; @@ -52,7 +52,7 @@ class CartonSizeWidget extends StatefulWidget { this.height, required this.sender, required this.consignee, - required this.deliveryType, + required this.lastMile, required this.billType}) : super(key: key); @@ -68,7 +68,7 @@ class _CartonSizeWidgetState extends State { List _shipments = []; CartonSize? _selectStandardSize; - late String _selectedDeliveryType; + late String _selectedLastmile; late String _billToValue; TextEditingController _widthController = new TextEditingController(); @@ -82,13 +82,10 @@ class _CartonSizeWidgetState extends State { } _init() async { - _selectedDeliveryType = widget.deliveryType; + _selectedLastmile = widget.lastMile; _billToValue = widget.billType; _cartonSizeType = widget.cartonSizeType; - List cartonSizes = context.read().cartonSizes; - _selectStandardSize = widget.standardSize ?? cartonSizes.first; - _lengthController.text = widget.length == null ? "0" : removeTrailingZeros(widget.length ?? 0); _widthController.text = @@ -96,6 +93,7 @@ class _CartonSizeWidgetState extends State { _heightController.text = widget.height == null ? "0" : removeTrailingZeros(widget.height ?? 0); + _getStandardCartonSize(); _loadShipment(); if (mounted) { @@ -103,11 +101,24 @@ class _CartonSizeWidgetState extends State { } } + _getStandardCartonSize() { + List cartonSizes = context.read().cartonSizes; + _selectStandardSize = widget.standardSize ?? cartonSizes.first; + if (mounted) { + setState(() {}); + } + } + _loadShipment() async { var fcsShipments = await context.read().getActiveFcsShipments(); _shipments = fcsShipments; - _shipment = widget.shipment; + if (_shipments.contains(widget.shipment)) { + _shipment = widget.shipment; + } else { + _shipment = null; + } + if (mounted) { setState(() {}); } @@ -116,6 +127,7 @@ class _CartonSizeWidgetState extends State { @override void didUpdateWidget(covariant CartonSizeWidget oldWidget) { _loadShipment(); + _getStandardCartonSize(); super.didUpdateWidget(oldWidget); } @@ -125,7 +137,7 @@ class _CartonSizeWidgetState extends State { context.watch().getCartonSizes; bool isStandardSize = _cartonSizeType == standardCarton; bool isCustomSize = _cartonSizeType == customCarton; - bool isNoneDefinedSize = _cartonSizeType == packageCartion; + bool isNoneDefinedSize = _cartonSizeType == packageCarton; final senderBox = DisplayText( text: widget.sender.name, @@ -139,7 +151,7 @@ class _CartonSizeWidgetState extends State { text: widget.consignee.name, labelTextKey: "box.consignee.title", iconData: MaterialCommunityIcons.account_arrow_left, - subText: Text(widget.consignee.fcsID!, + subText: Text(widget.consignee.fcsID ?? '', style: TextStyle(fontSize: 13, color: labelColor)), ); @@ -161,16 +173,16 @@ class _CartonSizeWidgetState extends State { child: InkWell( onTap: () { setState(() { - _selectedDeliveryType = delivery_caton; + _selectedLastmile = delivery_caton; }); }, child: Row(children: [ LocalRadio( value: delivery_caton, - groupValue: _selectedDeliveryType, + groupValue: _selectedLastmile, onChanged: (p0) { setState(() { - _selectedDeliveryType = delivery_caton; + _selectedLastmile = delivery_caton; }); }, ), @@ -179,7 +191,7 @@ class _CartonSizeWidgetState extends State { padding: const EdgeInsets.only(left: 10), child: LocalText(context, 'box.delivery', fontSize: 15, - color: _selectedDeliveryType == delivery_caton + color: _selectedLastmile == delivery_caton ? primaryColor : Colors.black), ), @@ -190,16 +202,16 @@ class _CartonSizeWidgetState extends State { child: InkWell( onTap: () { setState(() { - _selectedDeliveryType = pickup_carton; + _selectedLastmile = pickup_carton; }); }, child: Row(children: [ LocalRadio( value: pickup_carton, - groupValue: _selectedDeliveryType, + groupValue: _selectedLastmile, onChanged: (p0) { setState(() { - _selectedDeliveryType = pickup_carton; + _selectedLastmile = pickup_carton; }); }, ), @@ -208,7 +220,7 @@ class _CartonSizeWidgetState extends State { padding: const EdgeInsets.only(left: 10), child: LocalText(context, 'box.pickup', fontSize: 15, - color: _selectedDeliveryType == pickup_carton + color: _selectedLastmile == pickup_carton ? primaryColor : Colors.black), ), @@ -315,7 +327,7 @@ class _CartonSizeWidgetState extends State { if (widget.onContinue != null) { widget.onContinue!( - _selectedDeliveryType, _billToValue, _shipment!, _cartonSizeType, + _selectedLastmile, _billToValue, _shipment!, _cartonSizeType, standardSize: _selectStandardSize, length: l, width: w, height: h); } }); @@ -463,16 +475,16 @@ class _CartonSizeWidgetState extends State { InkWell( onTap: () { setState(() { - _cartonSizeType = packageCartion; + _cartonSizeType = packageCarton; }); }, child: Row(children: [ LocalRadio( - value: packageCartion, + value: packageCarton, groupValue: _cartonSizeType, onChanged: (p0) { setState(() { - _cartonSizeType = packageCartion; + _cartonSizeType = packageCarton; }); }, ), diff --git a/lib/pages/carton/carton_submit.dart b/lib/pages/carton/carton_submit.dart index 4224821..804dff8 100644 --- a/lib/pages/carton/carton_submit.dart +++ b/lib/pages/carton/carton_submit.dart @@ -30,7 +30,7 @@ class CartonSubmit extends StatelessWidget { final double length; final double width; final double height; - final String deliveryType; + final String lastMile; final List cargoTypes; final List surchareItems; final OnCreateCarton? onCreate; @@ -47,7 +47,7 @@ class CartonSubmit extends StatelessWidget { this.packages = const [], this.standardSize, required this.cartonSizeType, - required this.deliveryType, + required this.lastMile, this.length = 0, this.width = 0, this.height = 0, @@ -69,10 +69,7 @@ class CartonSubmit extends StatelessWidget { final cartonType = Padding( padding: const EdgeInsets.only(top: 10), child: SubmitTextWidget( - labelKey: 'box.carton.type', - text: carton_from_packages, - // subText: boxDimension - ), + labelKey: 'box.carton.type', text: carton_from_packages), ); final shipmentBox = Padding( @@ -187,7 +184,7 @@ class CartonSubmit extends StatelessWidget { padding: const EdgeInsets.only(top: 10), child: SubmitTextWidget( labelKey: 'box.select.delivery', - text: deliveryType, + text: lastMile == delivery_caton ? 'Delivery' : 'Pick-up', ), ); @@ -195,7 +192,7 @@ class CartonSubmit extends StatelessWidget { padding: const EdgeInsets.only(top: 10), child: SubmitTextWidget( labelKey: 'box.carton_size', - text: boxDimension??'', + text: boxDimension ?? 'No defined size', ), ); diff --git a/lib/pages/carton/custom_duty_addition.dart b/lib/pages/carton/custom_duty_addition.dart deleted file mode 100644 index d096b60..0000000 --- a/lib/pages/carton/custom_duty_addition.dart +++ /dev/null @@ -1,131 +0,0 @@ -import 'package:fcs/domain/entities/cargo_type.dart'; -import 'package:fcs/helpers/theme.dart'; -import 'package:fcs/pages/main/util.dart'; -import 'package:fcs/pages/rates/model/shipment_rate_model.dart'; -import 'package:fcs/pages/widgets/local_app_bar.dart'; -import 'package:fcs/pages/widgets/progress.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -import '../widgets/local_button.dart'; - -class CustomDutyAddition extends StatefulWidget { - final List customDuties; - - const CustomDutyAddition({super.key, required this.customDuties}); - @override - _CustomDutyAdditionState createState() => _CustomDutyAdditionState(); -} - -class _CustomDutyAdditionState extends State { - bool _isLoading = false; - List customDuties = []; - - @override - void initState() { - _init(); - super.initState(); - } - - _init() { - var shipmentRateModel = - Provider.of(context, listen: false); - customDuties = - shipmentRateModel.rate.customDuties.map((e) => e.clone()).toList(); - - for (var p in customDuties) { - p.qty = 0; - if (widget.customDuties.any((e) => e.id == p.id)) { - p.isChecked = true; - } else { - p.isChecked = false; - } - - widget.customDuties.forEach((vp) { - if (p.id == vp.id) { - p.qty = vp.qty; - } - }); - } - - if (mounted) { - setState(() {}); - } - } - - @override - Widget build(BuildContext context) { - List getCargoRowList(List _c) { - return _c.map((c) { - return Container( - child: Container( - padding: - EdgeInsets.only(left: 10.0, right: 5.0, top: 3.0, bottom: 3.0), - child: InkWell( - onTap: () { - setState(() { - c.isChecked = !c.isChecked; - }); - }, - child: Row( - children: [ - Checkbox( - value: c.isChecked, - activeColor: primaryColor, - onChanged: (bool? check) { - setState(() { - c.isChecked = check ?? false; - }); - }), - new Text(c.name ?? '', style: textStyle), - ], - ), - ), - ), - ); - }).toList(); - } - - final saveBtn = Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), - child: LocalButton( - textKey: "box.cargo.select.btn", - callBack: () { - List _cargos = - customDuties.where((c) => c.isChecked).toList(); - - if (_cargos.isEmpty) { - showMsgDialog(context, 'Error', "Please select the cargo type"); - return; - } - Navigator.pop(context, _cargos); - }, - ), - ); - - return LocalProgress( - inAsyncCall: _isLoading, - child: Scaffold( - appBar: LocalAppBar( - labelKey: 'box.select.cargo_type', - backgroundColor: Colors.white, - labelColor: primaryColor, - arrowColor: primaryColor), - body: Container( - padding: EdgeInsets.all(10), - child: ListView( - shrinkWrap: true, - children: [ - Column( - children: getCargoRowList(customDuties), - ), - SizedBox(height: 30), - saveBtn, - SizedBox(height: 20), - ], - ), - ), - ), - ); - } -} diff --git a/lib/pages/carton/mix_carton/mix_carton_editor.dart b/lib/pages/carton/mix_carton/mix_carton_editor.dart index 76b8bb4..26b6b00 100644 --- a/lib/pages/carton/mix_carton/mix_carton_editor.dart +++ b/lib/pages/carton/mix_carton/mix_carton_editor.dart @@ -12,6 +12,7 @@ import '../../../domain/entities/carton_size.dart'; import '../../../domain/entities/fcs_shipment.dart'; import '../../../domain/vo/local_step.dart'; import '../../../helpers/theme.dart'; +import '../../carton_size/model/carton_size_model.dart'; import '../../main/util.dart'; import '../../widgets/local_text.dart'; import '../../widgets/progress.dart'; @@ -58,10 +59,30 @@ class _MixCartonEditorState extends State { _init() async { context.read().clearSelection(); - _cartonSizeType = customCarton; - _length = widget.carton.length; - _width = widget.carton.width; - _height = widget.carton.height; + + // check carton size type + List cartonSizes = context.read().cartonSizes; + + var sameLength = + cartonSizes.any((size) => size.length == widget.carton.length); + var sameWidth = + cartonSizes.any((size) => size.width == widget.carton.width); + var sameHeight = + cartonSizes.any((size) => size.height == widget.carton.height); + bool isStandartSize = sameLength && sameWidth && sameHeight; + if (isStandartSize) { + _cartonSizeType = standardCarton; + _standardSize = cartonSizes.firstWhere((size) => + size.length == widget.carton.length && + size.width == widget.carton.width && + size.height == widget.carton.height); + } else if (widget.carton.length == 0 && + widget.carton.width == 0 && + widget.carton.height == 0) { + _cartonSizeType = packageCarton; + } else { + _cartonSizeType = customCarton; + } var s = await context .read() @@ -70,7 +91,7 @@ class _MixCartonEditorState extends State { _cartons = await context .read() - .getCartonsByIds(widget.carton.mixCartonIDs); + .getCartonsByIds(widget.carton.cartonIDs); if (mounted) { setState(() {}); @@ -113,8 +134,16 @@ class _MixCartonEditorState extends State { }, ), backgroundColor: Colors.white, - title: LocalText(context, 'box.update_title', - color: primaryColor, fontSize: 20), + title: Column( + children: [ + LocalText(context, 'box.update_title', + color: primaryColor, fontSize: 20), + Text( + widget.carton.cartonNumber ?? '', + style: TextStyle(color: primaryColor, fontSize: 14), + ) + ], + ), ), body: Column( children: [ @@ -212,6 +241,35 @@ class _MixCartonEditorState extends State { _isLoading = true; }); try { + double length = 0; + double width = 0; + double height = 0; + + if (_cartonSizeType == standardCarton) { + if (_standardSize != null) { + length = _standardSize!.length; + width = _standardSize!.width; + height = _standardSize!.height; + } + } else if (_cartonSizeType == customCarton) { + length = _length; + width = _width; + height = _height; + } else { + length = 0; + width = 0; + height = 0; + } + + var carton = Carton( + id: widget.carton.id, + cartonType: mix_carton, + fcsShipmentID: _shipment?.id, + length: length, + width: width, + height: height, + cartons: _cartons); + await context.read().updateCarton(carton); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); diff --git a/lib/pages/carton/mix_carton/mix_carton_form.dart b/lib/pages/carton/mix_carton/mix_carton_form.dart index b2925c3..2fea3f0 100644 --- a/lib/pages/carton/mix_carton/mix_carton_form.dart +++ b/lib/pages/carton/mix_carton/mix_carton_form.dart @@ -1,5 +1,6 @@ // ignore_for_file: deprecated_member_use +import 'package:fcs/pages/carton/model/carton_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; @@ -187,7 +188,35 @@ class _MixCartonFormState extends State { _isLoading = true; }); try { - Navigator.pop(context, true); + double length = 0; + double width = 0; + double height = 0; + + if (_cartonSizeType == standardCarton) { + if (_standardSize != null) { + length = _standardSize!.length; + width = _standardSize!.width; + height = _standardSize!.height; + } + } else if (_cartonSizeType == customCarton) { + length = _length; + width = _width; + height = _height; + } else { + length = 0; + width = 0; + height = 0; + } + + var carton = Carton( + cartonType: mix_carton, + fcsShipmentID: _shipment?.id, + length: length, + width: width, + height: height, + cartons: _cartons); + var c = await context.read().createMixCarton(carton); + Navigator.pop(context, c); } catch (e) { showMsgDialog(context, "Error", e.toString()); } finally { diff --git a/lib/pages/carton/mix_carton/mix_carton_submit.dart b/lib/pages/carton/mix_carton/mix_carton_submit.dart index 85a8fca..52e164d 100644 --- a/lib/pages/carton/mix_carton/mix_carton_submit.dart +++ b/lib/pages/carton/mix_carton/mix_carton_submit.dart @@ -110,17 +110,14 @@ class _MixCartonSubmitState extends State { final cartonType = Padding( padding: const EdgeInsets.only(top: 10), child: SubmitTextWidget( - labelKey: 'box.carton.type', - text: carton_mix_carton, - // subText: boxDimension, - ), + labelKey: 'box.carton.type', text: carton_mix_carton), ); final cartonSizeBox = Padding( padding: const EdgeInsets.only(top: 10), child: SubmitTextWidget( labelKey: 'box.carton_size', - text: boxDimension ?? '', + text: boxDimension ?? 'No defined size', ), ); diff --git a/lib/pages/carton/mix_carton/type_widget.dart b/lib/pages/carton/mix_carton/type_widget.dart index 598cbf6..34833e7 100644 --- a/lib/pages/carton/mix_carton/type_widget.dart +++ b/lib/pages/carton/mix_carton/type_widget.dart @@ -68,8 +68,6 @@ class _TypeWidgetState extends State { } _init() async { - List cartonSizes = context.read().cartonSizes; - _selectStandardSize = widget.standardSize ?? cartonSizes.first; _cartionSizeType = widget.cartonSizeType; _lengthController.text = @@ -79,6 +77,8 @@ class _TypeWidgetState extends State { _heightController.text = widget.height == null ? "0" : removeTrailingZeros(widget.height ?? 0); + _getStandardCartonSize(); + _loadShipment(); if (mounted) { @@ -86,11 +86,24 @@ class _TypeWidgetState extends State { } } + _getStandardCartonSize() { + List cartonSizes = context.read().cartonSizes; + _selectStandardSize = widget.standardSize ?? cartonSizes.first; + if (mounted) { + setState(() {}); + } + } + _loadShipment() async { var fcsShipments = await context.read().getActiveFcsShipments(); _shipments = fcsShipments; - _shipment = widget.shipment; + if (_shipments.contains(widget.shipment)) { + _shipment = widget.shipment; + } else { + _shipment = null; + } + if (mounted) { setState(() {}); } @@ -99,6 +112,7 @@ class _TypeWidgetState extends State { @override void didUpdateWidget(covariant TypeWidget oldWidget) { _loadShipment(); + _getStandardCartonSize(); super.didUpdateWidget(oldWidget); } @@ -109,7 +123,7 @@ class _TypeWidgetState extends State { bool isStandardSize = _cartionSizeType == standardCarton; bool isCustomSize = _cartionSizeType == customCarton; - bool isNoneDefinedSize = _cartionSizeType == packageCartion; + bool isNoneDefinedSize = _cartionSizeType == packageCarton; final continueBtn = ContinueButton(onTap: () { double l = double.tryParse(_lengthController.text) ?? 0; @@ -287,16 +301,16 @@ class _TypeWidgetState extends State { InkWell( onTap: () { setState(() { - _cartionSizeType = packageCartion; + _cartionSizeType = packageCarton; }); }, child: Row(children: [ LocalRadio( - value: packageCartion, + value: packageCarton, groupValue: _cartionSizeType, onChanged: (p0) { setState(() { - _cartionSizeType = packageCartion; + _cartionSizeType = packageCarton; }); }, ), diff --git a/lib/pages/carton/model/carton_model.dart b/lib/pages/carton/model/carton_model.dart index 95a2e97..49148c5 100644 --- a/lib/pages/carton/model/carton_model.dart +++ b/lib/pages/carton/model/carton_model.dart @@ -6,6 +6,7 @@ import 'package:fcs/data/services/services.dart'; import 'package:fcs/constants.dart'; import 'package:fcs/domain/entities/carton.dart'; import 'package:fcs/domain/entities/fcs_shipment.dart'; +import 'package:fcs/helpers/shared_pref.dart'; import 'package:fcs/pages/main/model/base_model.dart'; import 'package:fcs/pagination/paginator_listener.dart'; import 'package:logging/logging.dart'; @@ -18,8 +19,6 @@ class CartonModel extends BaseModel { final log = Logger('CartonModel'); var defaultShipment = FcsShipment(shipmentNumber: "All shipments", id: all); - - PaginatorListener? cartonsByFilter; PaginatorListener? getBoxes; String? filterByStatus; @@ -49,13 +48,10 @@ class CartonModel extends BaseModel { @override logout() async { getBoxes?.close(); - cartonsByFilter?.close(); } Future _initData() async { logout(); - - _loadPaginationCartons(); } filterCarton(FcsShipment? fcsShipment, User? consignee, User? sender, @@ -140,7 +136,7 @@ class CartonModel extends BaseModel { pageQuery = pageQuery.where("fcs_shipment_id", isEqualTo: shipment!.id); } - pageQuery = pageQuery.orderBy("created_at", descending: true); + pageQuery = pageQuery.orderBy("update_time", descending: true); getBoxes?.close(); getBoxes = PaginatorListener( @@ -148,30 +144,6 @@ class CartonModel extends BaseModel { rowPerLoad: 30); } - _loadPaginationCartons() { - if (user == null || !user!.hasCarton()) return null; - String path = "/$cartons_collection"; - - Query col = FirebaseFirestore.instance.collection(path); - // .where("carton_type", - // whereIn: [ - // carton_from_packages, - // carton_from_cartons - // ]).where("status", isEqualTo: carton_packed_status) - // ; - Query pageQuery = FirebaseFirestore.instance - .collection(path) - // .where("carton_type", - // whereIn: [carton_from_packages, carton_from_cartons]) - // .where("status", isEqualTo: carton_packed_status) - .orderBy("created_at", descending: true); - - cartonsByFilter?.close(); - cartonsByFilter = PaginatorListener( - col, pageQuery, (data, id) => Carton.fromMap(data, id), - rowPerLoad: 30); - } - Future> getCartons(String shipmentID) async { String path = "/$cartons_collection"; var querySnap = await FirebaseFirestore.instance @@ -235,8 +207,18 @@ class CartonModel extends BaseModel { return Services.instance.cartonService.deleteCarton(carton); } + Future createMixCarton(Carton carton) { + return Services.instance.cartonService.createCarton(carton); + } + + Future updateMixCarton(Carton carton) { + return Services.instance.cartonService.updateMixCarton(carton); + } + Future> searchCarton(String term) async { - + if (term != '') { + await SharedPref.saveRecentSearch('carton_search', term); + } return Services.instance.cartonService.searchCarton(term); } @@ -277,9 +259,11 @@ class CartonModel extends BaseModel { uploadedURL.forEach((url) { carton.photoUrls.add(url); }); + + carton.photoUrls.removeWhere((e) => deletedUrls.contains(e)); } try { - // await Services.instance.packageService.updateReceiving(package); + await Services.instance.cartonService.uploadCartonImages(carton); } catch (e) { // delete newly uploaded photos if fails try { diff --git a/lib/pages/carton/model/carton_selection_model.dart b/lib/pages/carton/model/carton_selection_model.dart index 00e4b45..c78f367 100644 --- a/lib/pages/carton/model/carton_selection_model.dart +++ b/lib/pages/carton/model/carton_selection_model.dart @@ -54,7 +54,7 @@ class CartonSelectionModel extends BaseModel { query = query.where("carton_number", isEqualTo: term); } - query = query.orderBy("created_at", descending: true); + query = query.orderBy("update_time", descending: true); if (_lastDocument != null) { query = query.startAfterDocument(_lastDocument!); diff --git a/lib/pages/carton/print_qr_code_page.dart b/lib/pages/carton/print_qr_code_page.dart index e3634f6..f751128 100644 --- a/lib/pages/carton/print_qr_code_page.dart +++ b/lib/pages/carton/print_qr_code_page.dart @@ -35,8 +35,11 @@ class PrintQrCodePage extends StatelessWidget { children: [ Text(carton.cartonNumber ?? "", style: TextStyle(fontSize: 18, fontFamily: "Roboto")), - Text(carton.userName!, - style: TextStyle(fontSize: 16, fontFamily: "Roboto")), + carton.consigneeName == null || carton.consigneeName == '' + ? const SizedBox() + : Text(carton.consigneeName!, + style: + TextStyle(fontSize: 16, fontFamily: "Roboto")), Padding( padding: const EdgeInsets.only(top: 3), child: Text("${carton.actualWeight} lb", diff --git a/lib/pages/carton/surcharge_item_addition.dart b/lib/pages/carton/surcharge_item_addition.dart new file mode 100644 index 0000000..3eb84dd --- /dev/null +++ b/lib/pages/carton/surcharge_item_addition.dart @@ -0,0 +1,114 @@ +import 'package:fcs/domain/entities/cargo_type.dart'; +import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/rates/model/shipment_rate_model.dart'; +import 'package:fcs/pages/widgets/local_app_bar.dart'; +import 'package:fcs/pages/widgets/progress.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../main/util.dart'; + +class SurchargeItemAddition extends StatefulWidget { + final List items; + const SurchargeItemAddition({super.key, required this.items}); + @override + _SurchargeItemAdditionState createState() => _SurchargeItemAdditionState(); +} + +class _SurchargeItemAdditionState extends State { + bool _isLoading = false; + List surchargeItems = []; + + @override + void initState() { + _init(); + super.initState(); + } + + _init() { + var shipmentRateModel = + Provider.of(context, listen: false); + surchargeItems = + shipmentRateModel.rate.customDuties.map((e) => e.clone()).toList(); + + for (var p in surchargeItems) { + p.qty = 0; + if (widget.items.any((e) => e.id == p.id)) { + p.isChecked = true; + } else { + p.isChecked = false; + } + + widget.items.forEach((vp) { + if (p.id == vp.id) { + p.qty = vp.qty; + } + }); + } + + if (mounted) { + setState(() {}); + } + } + + _onTap(CargoType cargo) { + if (cargo.isChecked) { + showMsgDialog(context, "Error", "Already exit!"); + return; + } + Navigator.pop(context, cargo); + } + + @override + Widget build(BuildContext context) { + List getCargoRowList(List _c) { + return _c.map((c) { + return ListTile( + onTap: () { + _onTap(c); + }, + title: new Text(c.name ?? '', style: textStyle), + trailing: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: primaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + minimumSize: Size(80, 35)), + onPressed: c.isChecked + ? null + : () { + _onTap(c); + }, + child: Text( + "Add", + style: TextStyle(color: Colors.white, fontSize: 14), + ), + )); + }).toList(); + } + + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: LocalAppBar( + labelKey: 'box.select.cargo_type', + backgroundColor: Colors.white, + labelColor: primaryColor, + arrowColor: primaryColor), + body: Container( + padding: EdgeInsets.all(10), + child: ListView( + shrinkWrap: true, + children: [ + Column( + children: getCargoRowList(surchargeItems), + ), + SizedBox(height: 30), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/carton/widget/carton_list_row.dart b/lib/pages/carton/widget/carton_list_row.dart index 08df328..56c1a4a 100644 --- a/lib/pages/carton/widget/carton_list_row.dart +++ b/lib/pages/carton/widget/carton_list_row.dart @@ -56,7 +56,7 @@ class CartonListRow extends StatelessWidget { Padding( padding: const EdgeInsets.only(top: 5), child: new Text( - box.userName ?? "", + box.consigneeName ?? "", style: new TextStyle( fontSize: 15.0, color: Colors.grey), ), diff --git a/lib/pages/carton_search/carton_list_row.dart b/lib/pages/carton_search/carton_list_row.dart index 10cf69a..0621c24 100644 --- a/lib/pages/carton_search/carton_list_row.dart +++ b/lib/pages/carton_search/carton_list_row.dart @@ -62,7 +62,7 @@ class CartonListRow extends StatelessWidget { Padding( padding: const EdgeInsets.only(top: 5), child: new Text( - carton.userName ?? "", + carton.consigneeName ?? "", style: new TextStyle( fontSize: 15.0, color: Colors.grey), ), diff --git a/lib/pages/carton_search/carton_search.dart b/lib/pages/carton_search/carton_search.dart index eeecc2c..f13dc60 100644 --- a/lib/pages/carton_search/carton_search.dart +++ b/lib/pages/carton_search/carton_search.dart @@ -7,6 +7,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:provider/provider.dart'; +import '../../helpers/shared_pref.dart'; +import '../widgets/suggest_list.dart'; import 'carton_list_row.dart'; typedef CallbackCartonSelect(Carton carton); @@ -32,7 +34,7 @@ class PartSearchDelegate extends SearchDelegate { appBarTheme: AppBarTheme(color: primaryColor), iconButtonTheme: IconButtonThemeData( style: ButtonStyle( - iconColor: MaterialStateProperty.all(Colors.white))), + iconColor: WidgetStateProperty.all(Colors.white))), inputDecorationTheme: InputDecorationTheme( border: InputBorder.none, hintStyle: TextStyle(color: Colors.grey, fontSize: 14)), @@ -120,13 +122,27 @@ class PartSearchDelegate extends SearchDelegate { @override Widget buildSuggestions(BuildContext context) { - return Container( - child: Center( - child: Opacity( - opacity: 0.2, - child: Icon(MaterialCommunityIcons.package, - size: 200, color: primaryColor)), - ), + return FutureBuilder?>( + future: SharedPref.getRecentSearch('carton_search', query), + builder: (context, snapshot) { + List _oldFilters = snapshot.data ?? []; + if (_oldFilters.isEmpty) { + return const Center( + child: Opacity( + opacity: 0.2, + child: Icon(MaterialCommunityIcons.package, + color: primaryColor, size: 200), + ), + ); + } + return SuggestList( + recentSearchList: _oldFilters, + onTap: (String s) { + query = s; + showResults(context); + }, + prefKey: 'carton_search'); + }, ); } diff --git a/lib/pages/delivery/delivery_info.dart b/lib/pages/delivery/delivery_info.dart index 757a59d..829cb6f 100644 --- a/lib/pages/delivery/delivery_info.dart +++ b/lib/pages/delivery/delivery_info.dart @@ -86,10 +86,10 @@ class _DeliveryInfoState extends State { _loadPackages() async { if (!isFromPackages && !isSmallBag) return; - if (_box.cartonType == carton_from_packages && _box.userID == null) return; + if (_box.cartonType == carton_from_packages && _box.consigneeID == null) return; PackageModel packageModel = Provider.of(context, listen: false); - List packages = await packageModel.getPackages(_box.userID!, [ + List packages = await packageModel.getPackages(_box.consigneeID!, [ package_processed_status, package_packed_status, package_shipped_status, @@ -137,13 +137,13 @@ class _DeliveryInfoState extends State { iconData: Ionicons.ios_airplane, ); final fcsIDBox = DisplayText( - text: _box.fcsID, + text: _box.consigneeFCSID, labelTextKey: "box.fcs.id", icon: FcsIDIcon(), ); final customerNameBox = DisplayText( - text: _box.userName, + text: _box.consigneeName, labelTextKey: "box.name", iconData: Icons.person, ); diff --git a/lib/pages/delivery/delivery_list_row.dart b/lib/pages/delivery/delivery_list_row.dart index 6466003..32862e3 100644 --- a/lib/pages/delivery/delivery_list_row.dart +++ b/lib/pages/delivery/delivery_list_row.dart @@ -48,7 +48,7 @@ class DeliveryListRow extends StatelessWidget { Padding( padding: const EdgeInsets.only(top: 5), child: new Text( - box.userName ?? "", + box.consigneeName ?? "", style: new TextStyle( fontSize: 15.0, color: Colors.grey), ), diff --git a/lib/pages/fcs_shipment/fcs_shipment_editor.dart b/lib/pages/fcs_shipment/fcs_shipment_editor.dart index e430131..4114ebe 100644 --- a/lib/pages/fcs_shipment/fcs_shipment_editor.dart +++ b/lib/pages/fcs_shipment/fcs_shipment_editor.dart @@ -1,4 +1,6 @@ import 'package:fcs/domain/entities/fcs_shipment.dart'; +import 'package:fcs/domain/entities/shipment_consignee.dart'; +import 'package:fcs/domain/entities/shipment_port.dart'; import 'package:fcs/helpers/theme.dart'; import 'package:fcs/localization/app_translations.dart'; import 'package:fcs/pages/fcs_shipment/model/fcs_shipment_model.dart'; @@ -30,16 +32,16 @@ class _FcsShipmentEditorState extends State { var dateFormatter = new DateFormat('dd MMM yyyy'); TextEditingController _shipmentNumberController = new TextEditingController(); TextEditingController _cutoffDateController = new TextEditingController(); - TextEditingController _arrivalDateController = new TextEditingController(); - TextEditingController _departureDateControler = new TextEditingController(); - TextEditingController _consigneeController = new TextEditingController(); - TextEditingController _portController = new TextEditingController(); - TextEditingController _destinationController = new TextEditingController(); + TextEditingController _etaDateController = new TextEditingController(); + TextEditingController _statusController = new TextEditingController(); FcsShipment _shipment = new FcsShipment(); bool _isLoading = false; ShipmentType? _currentShipmentType; + ShipmentConsignee? _currentConsignee; + ShipmentPort? _currentLoadingPort; + ShipmentPort? _currentDestination; bool _isNew = false; @override @@ -53,24 +55,38 @@ class _FcsShipmentEditorState extends State { if (_shipment.cutoffDate != null) _cutoffDateController.text = dateFormatter.format(_shipment.cutoffDate!); - if (_shipment.arrivalDate != null) - _arrivalDateController.text = - dateFormatter.format(_shipment.arrivalDate!); - if (_shipment.departureDate != null) - _departureDateControler.text = - dateFormatter.format(_shipment.departureDate!); - _statusController.text = _shipment.status ?? ""; - _consigneeController.text = _shipment.consignee ?? ""; - _portController.text = _shipment.port ?? ""; - _destinationController.text = _shipment.destination ?? ""; + if (_shipment.etaDate != null) + _etaDateController.text = dateFormatter.format(_shipment.etaDate!); - List list = model.shipmentTypes + _statusController.text = _shipment.status ?? ""; + + List shipmentTypes = model.shipmentTypes .where((e) => e.id == _shipment.shipmentTypeId) .toList(); + _currentShipmentType = + shipmentTypes.isNotEmpty ? shipmentTypes.first : null; - _currentShipmentType = list.isNotEmpty ? list.first : null; + List shipmentConsingees = model.shipmentConsingees + .where((e) => e.id == _shipment.consigneeId) + .toList(); + _currentConsignee = + shipmentConsingees.isNotEmpty ? shipmentConsingees.first : null; + + List loadingPorts = model.shipmentPorts + .where((e) => e.id == _shipment.loadingPortId) + .toList(); + _currentLoadingPort = loadingPorts.isNotEmpty ? loadingPorts.first : null; + + List loadingDestination = model.shipmentPorts + .where((e) => e.id == _shipment.destinationPortId) + .toList(); + _currentDestination = + loadingDestination.isNotEmpty ? loadingDestination.first : null; } else { - _currentShipmentType = model.shipmentTypes[0]; + _currentShipmentType = model.shipmentTypes.first; + _currentConsignee = model.shipmentConsingees.first; + _currentLoadingPort = model.shipmentPorts.first; + _currentDestination = model.shipmentPorts.first; } } @@ -82,8 +98,111 @@ class _FcsShipmentEditorState extends State { @override Widget build(BuildContext context) { var languageModel = Provider.of(context); - List shipmentTypes = - context.watch().shipmentTypes; + var shipmentModel = context.watch(); + List shipmentTypes = shipmentModel.shipmentTypes; + List shipmentConsingees = + shipmentModel.shipmentConsingees; + List shipmentPorts = shipmentModel.shipmentPorts; + + final shipmentTypeBox = DropdownButtonFormField( + value: _currentShipmentType == "" ? null : _currentShipmentType, + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + enabledBorder: + UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)), + focusedBorder: + UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)), + fillColor: Colors.white, + labelStyle: languageModel.isEng + ? newLabelStyle(color: Colors.black54, fontSize: 20) + : newLabelStyleMM(color: Colors.black54, fontSize: 20), + labelText: + AppTranslations.of(context)!.text('FCSshipment.shipment_type'), + icon: Icon(Ionicons.ios_airplane, color: primaryColor)), + items: shipmentTypes + .map((e) => DropdownMenuItem(child: Text(e.name), value: e)) + .toList(), + onChanged: (selected) => { + setState(() { + _currentShipmentType = selected; + }) + }, + ); + + final consigneeBox = DropdownButtonFormField( + value: _currentConsignee == "" ? null : _currentConsignee, + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + enabledBorder: + UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)), + focusedBorder: + UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)), + fillColor: Colors.white, + labelStyle: languageModel.isEng + ? newLabelStyle(color: Colors.black54, fontSize: 20) + : newLabelStyleMM(color: Colors.black54, fontSize: 20), + labelText: AppTranslations.of(context)!.text('FCSshipment.consignee'), + icon: Icon(Icons.work, color: primaryColor)), + items: shipmentConsingees + .map((e) => DropdownMenuItem(child: Text(e.name), value: e)) + .toList(), + onChanged: (selected) => { + setState(() { + _currentConsignee = selected; + }) + }, + ); + + final loadingPortBox = DropdownButtonFormField( + value: _currentLoadingPort == "" ? null : _currentLoadingPort, + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + enabledBorder: + UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)), + focusedBorder: + UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)), + fillColor: Colors.white, + labelStyle: languageModel.isEng + ? newLabelStyle(color: Colors.black54, fontSize: 20) + : newLabelStyleMM(color: Colors.black54, fontSize: 20), + labelText: + AppTranslations.of(context)!.text('FCSshipment.port_of_loading'), + icon: Icon(FontAwesomeIcons.ship, color: primaryColor)), + items: shipmentPorts + .map((e) => DropdownMenuItem(child: Text(e.name), value: e)) + .toList(), + onChanged: (selected) => { + setState(() { + _currentLoadingPort = selected; + }) + }, + ); + + final destinationBox = DropdownButtonFormField( + value: _currentDestination == "" ? null : _currentDestination, + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + enabledBorder: + UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)), + focusedBorder: + UnderlineInputBorder(borderSide: BorderSide(color: primaryColor)), + fillColor: Colors.white, + labelStyle: languageModel.isEng + ? newLabelStyle(color: Colors.black54, fontSize: 20) + : newLabelStyleMM(color: Colors.black54, fontSize: 20), + labelText: AppTranslations.of(context)! + .text('FCSshipment.final_destination'), + icon: + Icon(MaterialCommunityIcons.location_enter, color: primaryColor)), + items: shipmentPorts + .map((e) => DropdownMenuItem(child: Text(e.name), value: e)) + .toList(), + onChanged: (selected) => { + setState(() { + _currentDestination = selected; + }) + }, + ); final createBtn = Padding( padding: const EdgeInsets.symmetric(horizontal: 30), @@ -150,7 +269,7 @@ class _FcsShipmentEditorState extends State { InputDate( labelTextKey: "FCSshipment.ETA", iconData: Icons.date_range, - controller: _arrivalDateController, + controller: _etaDateController, autovalidateMode: AutovalidateMode.onUserInteraction, validator: (value) { if (value!.isEmpty) { @@ -160,47 +279,14 @@ class _FcsShipmentEditorState extends State { }, ), const SizedBox(height: 20), - DropdownButtonFormField( - value: - _currentShipmentType == "" ? null : _currentShipmentType, - decoration: InputDecoration( - contentPadding: EdgeInsets.zero, - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: primaryColor)), - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: primaryColor)), - fillColor: Colors.white, - labelStyle: languageModel.isEng - ? newLabelStyle(color: Colors.black54, fontSize: 20) - : newLabelStyleMM( - color: Colors.black54, fontSize: 20), - labelText: AppTranslations.of(context)! - .text('FCSshipment.shipment_type'), - icon: Icon(Ionicons.ios_airplane, color: primaryColor)), - items: shipmentTypes - .map((e) => - DropdownMenuItem(child: Text(e.name), value: e)) - .toList(), - onChanged: (selected) => { - setState(() { - _currentShipmentType = selected; - }) - }, - ), - const SizedBox(height: 5), - InputText( - labelTextKey: 'FCSshipment.consignee', - iconData: Icons.work, - controller: _consigneeController), - InputText( - labelTextKey: 'FCSshipment.port_of_loading', - iconData: FontAwesomeIcons.ship, - controller: _portController), - InputText( - labelTextKey: 'FCSshipment.final_destination', - iconData: MaterialCommunityIcons.location_enter, - controller: _destinationController), - SizedBox(height: 20), + shipmentTypeBox, + const SizedBox(height: 25), + consigneeBox, + const SizedBox(height: 25), + loadingPortBox, + const SizedBox(height: 25), + destinationBox, + SizedBox(height: 30), _isNew ? createBtn : updateBtn, SizedBox(height: 15) ]), @@ -214,16 +300,15 @@ class _FcsShipmentEditorState extends State { fcsShipment.id = _shipment.id; fcsShipment.shipmentNumber = _shipmentNumberController.text; fcsShipment.shipmentTypeId = _currentShipmentType?.id ?? ""; - fcsShipment.consignee = _consigneeController.text; - fcsShipment.port = _portController.text; - fcsShipment.destination = _destinationController.text; + fcsShipment.consigneeId = _currentConsignee?.id ?? ""; + fcsShipment.loadingPortId = _currentLoadingPort?.id ?? ""; + fcsShipment.destinationPortId = _currentDestination?.id ?? ""; var cutoffDate = _cutoffDateController.text; - var arrivalDate = _arrivalDateController.text; + var etaDate = _etaDateController.text; fcsShipment.cutoffDate = (cutoffDate == "" ? null : dateFormatter.parse(cutoffDate))!; - fcsShipment.arrivalDate = - (arrivalDate == "" ? null : dateFormatter.parse(arrivalDate))!; + fcsShipment.etaDate = (etaDate == "" ? null : dateFormatter.parse(etaDate)); return fcsShipment; } @@ -271,11 +356,11 @@ class _FcsShipmentEditorState extends State { var shipmentModel = Provider.of(context, listen: false); return _shipmentNumberController.text != "" || _cutoffDateController.text != "" || - _arrivalDateController.text != "" || - _consigneeController.text != "" || - _portController.text != "" || - _destinationController.text != "" || - _currentShipmentType != shipmentModel.shipmentTypes[0]; + _etaDateController.text != "" || + _currentConsignee != shipmentModel.shipmentConsingees.first || + _currentShipmentType != shipmentModel.shipmentTypes.first || + _currentLoadingPort != shipmentModel.shipmentPorts.first || + _currentDestination != shipmentModel.shipmentPorts.first; } else { FcsShipment fcsShipment = _getPayload(); return widget.shipment!.isChangedForEdit(fcsShipment); diff --git a/lib/pages/fcs_shipment/fcs_shipment_info.dart b/lib/pages/fcs_shipment/fcs_shipment_info.dart index 8982566..f642a68 100644 --- a/lib/pages/fcs_shipment/fcs_shipment_info.dart +++ b/lib/pages/fcs_shipment/fcs_shipment_info.dart @@ -20,8 +20,8 @@ import 'package:provider/provider.dart'; import 'fcs_shipment_editor.dart'; class FcsShipmentInfo extends StatefulWidget { - final FcsShipment? fcsShipment; - FcsShipmentInfo({this.fcsShipment}); + final FcsShipment fcsShipment; + FcsShipmentInfo({required this.fcsShipment}); @override _FcsShipmentInfoState createState() => _FcsShipmentInfoState(); @@ -29,7 +29,7 @@ class FcsShipmentInfo extends StatefulWidget { class _FcsShipmentInfoState extends State { var dateFormatter = new DateFormat('dd MMM yyyy'); - FcsShipment? _fcsShipment; + late FcsShipment _fcsShipment; bool _isLoading = false; TextEditingController _shipmentNumberController = new TextEditingController(); TextEditingController _cutoffDateController = new TextEditingController(); @@ -49,21 +49,21 @@ class _FcsShipmentInfoState extends State { } _load() { - _shipmentNumberController.text = _fcsShipment?.shipmentNumber ?? ""; - if (_fcsShipment?.cutoffDate != null) + _shipmentNumberController.text = _fcsShipment.shipmentNumber ?? ""; + if (_fcsShipment.cutoffDate != null) _cutoffDateController.text = - dateFormatter.format(_fcsShipment!.cutoffDate!); - if (_fcsShipment?.arrivalDate != null) - _arrivalDateController.text = - dateFormatter.format(_fcsShipment!.arrivalDate!); - if (_fcsShipment?.departureDate != null) + dateFormatter.format(_fcsShipment.cutoffDate!); + if (_fcsShipment.etaDate != null) + _arrivalDateController.text = dateFormatter.format(_fcsShipment.etaDate!); + if (_fcsShipment.departureDate != null) _departureDateControler.text = - dateFormatter.format(_fcsShipment!.departureDate!); - _shipmentTypeControler.text = _fcsShipment!.shipTypeName ?? ""; - _consigneeController.text = _fcsShipment!.consignee ?? ""; - _portController.text = _fcsShipment!.port ?? ""; - _destinationController.text = _fcsShipment!.destination ?? ""; - _statusController.text = _fcsShipment!.status ?? ""; + dateFormatter.format(_fcsShipment.departureDate!); + + _shipmentTypeControler.text = _fcsShipment.shipmentTypeName ?? ""; + _consigneeController.text = _fcsShipment.consigneeName ?? ''; + _portController.text = _fcsShipment.loadingPortName ?? ''; + _destinationController.text = _fcsShipment.destinationPortName ?? ''; + _statusController.text = _fcsShipment.status ?? ""; } @override @@ -176,7 +176,7 @@ class _FcsShipmentInfoState extends State { labelColor: primaryColor, arrowColor: primaryColor, actions: [ - _fcsShipment?.status == fcs_shipment_pending_status + _fcsShipment.status == fcs_shipment_pending_status ? IconButton( icon: Icon(Icons.edit, color: primaryColor), onPressed: _edit, @@ -212,20 +212,20 @@ class _FcsShipmentInfoState extends State { portBox, destinationBox, const SizedBox(height: 30), - _fcsShipment?.status == fcs_shipment_pending_status + _fcsShipment.status == fcs_shipment_pending_status ? processBtn : Container(), - _fcsShipment?.status == fcs_shipment_pending_status + _fcsShipment.status == fcs_shipment_pending_status ? Container( padding: EdgeInsets.only(top: 3), child: cancelBtn) : Container(), - _fcsShipment?.status == fcs_shipment_processed_status + _fcsShipment.status == fcs_shipment_processed_status ? shipBtn : Container(), - _fcsShipment?.status == fcs_shipment_shipped_status + _fcsShipment.status == fcs_shipment_shipped_status ? arriveBtn : Container(), - _fcsShipment?.status == fcs_shipment_arrived_status + _fcsShipment.status == fcs_shipment_arrived_status ? invoiceBtn : Container(), SizedBox(height: 20) @@ -244,8 +244,8 @@ class _FcsShipmentInfoState extends State { ); if (updated ?? false) { var shipmentModel = Provider.of(context, listen: false); - if (_fcsShipment != null && _fcsShipment!.id != null) { - FcsShipment? f = await shipmentModel.getFcsShipment(_fcsShipment!.id!); + if (_fcsShipment.id != null) { + FcsShipment? f = await shipmentModel.getFcsShipment(_fcsShipment.id!); if (f == null) return; setState(() { _fcsShipment = f; @@ -294,7 +294,7 @@ class _FcsShipmentInfoState extends State { _isLoading = true; }); try { - await context.read().process(_fcsShipment!.id!); + await context.read().process(_fcsShipment.id!); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); @@ -316,7 +316,7 @@ class _FcsShipmentInfoState extends State { _isLoading = true; }); try { - await context.read().ship(_fcsShipment!.id!); + await context.read().ship(_fcsShipment.id!); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); @@ -338,7 +338,7 @@ class _FcsShipmentInfoState extends State { _isLoading = true; }); try { - await context.read().arrive(_fcsShipment!.id!); + await context.read().arrive(_fcsShipment.id!); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); @@ -360,7 +360,7 @@ class _FcsShipmentInfoState extends State { _isLoading = true; }); try { - await context.read().invoice(_fcsShipment!.id!); + await context.read().invoice(_fcsShipment.id!); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); @@ -376,7 +376,7 @@ class _FcsShipmentInfoState extends State { _isLoading = true; }); try { - await context.read().cancel(_fcsShipment!.id!); + await context.read().cancel(_fcsShipment.id!); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); @@ -402,11 +402,11 @@ class _FcsShipmentInfoState extends State { } else if (id == 4) { reportName = "manifest"; } - _fcsShipment!.reportName = reportName; + _fcsShipment.reportName = reportName; FcsShipmentModel fcsShipmentModel = Provider.of(context, listen: false); - String url = await fcsShipmentModel.report(_fcsShipment!); + String url = await fcsShipmentModel.report(_fcsShipment); Navigator.of(context).push(CupertinoPageRoute( builder: (context) => PDFScreen( title: "", diff --git a/lib/pages/fcs_shipment/fcs_shipment_list.dart b/lib/pages/fcs_shipment/fcs_shipment_list.dart index fff5b4a..2bb9713 100644 --- a/lib/pages/fcs_shipment/fcs_shipment_list.dart +++ b/lib/pages/fcs_shipment/fcs_shipment_list.dart @@ -43,21 +43,21 @@ class _FcsShipmentListState extends State { return LocalProgress( inAsyncCall: _isLoading, child: Scaffold( - appBar: LocalAppBar(labelKey: "FCSshipment.list.title", actions: [ - _menuFilteringWidget(context), - ]), - floatingActionButton: FloatingActionButton.extended( - onPressed: () { - _newShipment(); - }, - icon: Icon(Icons.add), - label: - LocalText(context, "FCSshipment.add", color: Colors.white), - backgroundColor: primaryColor), - body: PaginatorListView( - paginatorListener: shipmentModel.fcsShipments!, - rowBuilder: (p) => FcsShipmentListRow(shipment: p), - color: primaryColor))); + appBar: LocalAppBar(labelKey: "FCSshipment.list.title", actions: [ + _menuFilteringWidget(context), + ]), + floatingActionButton: FloatingActionButton.extended( + onPressed: () { + _newShipment(); + }, + icon: Icon(Icons.add), + label: LocalText(context, "FCSshipment.add", color: Colors.white), + backgroundColor: primaryColor), + body: PaginatorListView( + paginatorListener: shipmentModel.fcsShipments!, + rowBuilder: (p) => FcsShipmentListRow(shipment: p), + color: primaryColor), + )); } _newShipment() { @@ -107,7 +107,9 @@ class _FcsShipmentListState extends State { value: choice, child: Row( children: [ - Flexible(child: Text("${choice.text}",style: TextStyle(color: Colors.black))), + Flexible( + child: Text("${choice.text}", + style: TextStyle(color: Colors.black))), const SizedBox( width: 10, ), diff --git a/lib/pages/fcs_shipment/model/fcs_shipment_model.dart b/lib/pages/fcs_shipment/model/fcs_shipment_model.dart index fe0c4b7..fe2863c 100644 --- a/lib/pages/fcs_shipment/model/fcs_shipment_model.dart +++ b/lib/pages/fcs_shipment/model/fcs_shipment_model.dart @@ -4,9 +4,11 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:fcs/data/services/services.dart'; import 'package:fcs/constants.dart'; import 'package:fcs/domain/entities/fcs_shipment.dart'; +import 'package:fcs/domain/entities/shipment_port.dart'; import 'package:fcs/pages/main/model/base_model.dart'; import 'package:logging/logging.dart'; +import '../../../domain/entities/shipment_consignee.dart'; import '../../../domain/entities/shipment_type.dart'; import '../../../pagination/paginator_listener.dart'; @@ -17,7 +19,26 @@ class FcsShipmentModel extends BaseModel { int selectedIndex = 0; List shipmentTypes = []; + List shipmentConsingees = []; + List shipmentPorts = []; StreamSubscription? _shipmentTypeListener; + StreamSubscription? _shipmentConsigneeListener; + StreamSubscription? _shipmentPortListener; + + ShipmentType? getShipmentType(id) { + var list = shipmentTypes.where((e) => e.id == id).toList(); + return list.isNotEmpty ? list.first : null; + } + + ShipmentConsignee? getShipmentConsignee(id) { + var list = shipmentConsingees.where((e) => e.id == id).toList(); + return list.isNotEmpty ? list.first : null; + } + + ShipmentPort? getShipmentPort(id) { + var list = shipmentPorts.where((e) => e.id == id).toList(); + return list.isNotEmpty ? list.first : null; + } onChanged(int index) { selectedIndex = index; @@ -39,7 +60,7 @@ class FcsShipmentModel extends BaseModel { pageQuery.where("status", isEqualTo: fcs_shipment_pending_status); } - // processing status + // processed status if (index == 2) { col = col.where("status", isEqualTo: fcs_shipment_processed_status); pageQuery = @@ -74,7 +95,7 @@ class FcsShipmentModel extends BaseModel { pageQuery.where("status", isEqualTo: fcs_shipment_canceled_status); } - pageQuery = pageQuery.orderBy("shipment_number", descending: true); + pageQuery = pageQuery.orderBy("update_time", descending: true); fcsShipments?.close(); fcsShipments = PaginatorListener( @@ -87,7 +108,7 @@ class FcsShipmentModel extends BaseModel { try { var snaps = await FirebaseFirestore.instance .collection("/$fcs_shipment_collection") - // .where("status", isEqualTo: fcs_shipment_processed_status) + .where("status", isEqualTo: fcs_shipment_processed_status) .get(const GetOptions(source: Source.server)); fcsShipments = snaps.docs.map((documentSnapshot) { var fcs = @@ -137,13 +158,19 @@ class FcsShipmentModel extends BaseModel { void initUser(user) { super.initUser(user); _loadShipmentTypes(); + _loadShipmentConsignees(); + _loadShipmentPorts(); } @override logout() async { fcsShipments?.close(); _shipmentTypeListener?.cancel(); + _shipmentConsigneeListener?.cancel(); + _shipmentPortListener?.cancel(); shipmentTypes.clear(); + shipmentConsingees.clear(); + shipmentPorts.clear(); } Future _loadShipmentTypes() async { @@ -155,10 +182,52 @@ class FcsShipmentModel extends BaseModel { .listen((QuerySnapshot snapshot) { shipmentTypes.clear(); shipmentTypes = snapshot.docs.map((documentSnapshot) { - var privilege = ShipmentType.fromMap( + var type = ShipmentType.fromMap( documentSnapshot.data() as Map, documentSnapshot.id); - return privilege; + return type; + }).toList(); + notifyListeners(); + }); + } catch (e) { + log.warning("Error!! $e"); + } + } + + Future _loadShipmentConsignees() async { + try { + _shipmentConsigneeListener = FirebaseFirestore.instance + .collection( + "/$config_collection/$setting_doc_id/$shipment_consignee_collection") + .snapshots() + .listen((QuerySnapshot snapshot) { + shipmentConsingees.clear(); + shipmentConsingees = snapshot.docs.map((documentSnapshot) { + var consignee = ShipmentConsignee.fromMap( + documentSnapshot.data() as Map, + documentSnapshot.id); + return consignee; + }).toList(); + notifyListeners(); + }); + } catch (e) { + log.warning("Error!! $e"); + } + } + + Future _loadShipmentPorts() async { + try { + _shipmentPortListener = FirebaseFirestore.instance + .collection( + "/$config_collection/$setting_doc_id/$shipment_port_collection") + .snapshots() + .listen((QuerySnapshot snapshot) { + shipmentPorts.clear(); + shipmentPorts = snapshot.docs.map((documentSnapshot) { + var port = ShipmentPort.fromMap( + documentSnapshot.data() as Map, + documentSnapshot.id); + return port; }).toList(); notifyListeners(); }); @@ -192,7 +261,7 @@ class FcsShipmentModel extends BaseModel { Future invoice(String id) { return Services.instance.fcsShipmentService - .updateFcsShipmentStatus(id, fcs_shipment_shipped_status); + .updateFcsShipmentStatus(id, fcs_shipment_invoiced_status); } Future cancel(String id) { diff --git a/lib/pages/package/model/package_model.dart b/lib/pages/package/model/package_model.dart index 92cefeb..10e6a4d 100644 --- a/lib/pages/package/model/package_model.dart +++ b/lib/pages/package/model/package_model.dart @@ -224,6 +224,9 @@ class PackageModel extends BaseModel { } Future> searchPackage(String term) async { + if (term != '') { + await SharedPref.saveRecentSearch('package_search', term); + } Future> packages = Services.instance.packageService.searchPackage(term); diff --git a/lib/pages/package_search/package_search.dart b/lib/pages/package_search/package_search.dart index b3e6b85..2b9930c 100644 --- a/lib/pages/package_search/package_search.dart +++ b/lib/pages/package_search/package_search.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:provider/provider.dart'; +import '../../helpers/shared_pref.dart'; +import '../widgets/suggest_list.dart'; + Future searchPackage(BuildContext context, {CallbackPackageSelect? callbackPackageSelect}) async => await showSearch( @@ -22,7 +25,7 @@ class PackageSearchDelegate extends SearchDelegate { PackageSearchDelegate({this.callbackPackageSelect}); @override - String get searchFieldLabel => 'Search by Tracking ID/Customer Name'; + String get searchFieldLabel => 'Search by Tracking ID/Customer name'; @override ThemeData appBarTheme(BuildContext context) { @@ -31,7 +34,7 @@ class PackageSearchDelegate extends SearchDelegate { appBarTheme: AppBarTheme(color: primaryColor), iconButtonTheme: IconButtonThemeData( style: ButtonStyle( - iconColor: MaterialStateProperty.all(Colors.white))), + iconColor: WidgetStateProperty.all(Colors.white))), inputDecorationTheme: InputDecorationTheme( border: InputBorder.none, hintStyle: TextStyle(color: Colors.grey, fontSize: 14)), @@ -123,12 +126,27 @@ class PackageSearchDelegate extends SearchDelegate { @override Widget buildSuggestions(BuildContext context) { - return Container( - child: Center( - child: Opacity( - opacity: 0.2, - child: Icon(Octicons.package, size: 200, color: primaryColor)), - ), + return FutureBuilder?>( + future: SharedPref.getRecentSearch('package_search', query), + builder: (context, snapshot) { + List _oldFilters = snapshot.data ?? []; + if (_oldFilters.isEmpty) { + return const Center( + child: Opacity( + opacity: 0.2, + child: Icon(Octicons.package, + color: primaryColor, size: 200), + ), + ); + } + return SuggestList( + recentSearchList: _oldFilters, + onTap: (String s) { + query = s; + showResults(context); + }, + prefKey: 'package_search'); + }, ); } diff --git a/lib/pages/pickup/model/pickup_model.dart b/lib/pages/pickup/model/pickup_model.dart index 7a5d12f..8d0665e 100644 --- a/lib/pages/pickup/model/pickup_model.dart +++ b/lib/pages/pickup/model/pickup_model.dart @@ -11,6 +11,8 @@ import 'package:fcs/pagination/paginator_listener.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as Path; +import '../../../helpers/shared_pref.dart'; + class PickupModel extends BaseModel { final log = Logger('PickupModel'); PaginatorListener? pickups; @@ -86,6 +88,9 @@ class PickupModel extends BaseModel { } Future> searchPickup(String term) async { + if (term != '') { + await SharedPref.saveRecentSearch('pickup_search', term); + } Future> pickups = Services.instance.pickupService.searchPickup(term); return pickups; diff --git a/lib/pages/pickup_search/pickup_search.dart b/lib/pages/pickup_search/pickup_search.dart index a167ce2..516d34b 100644 --- a/lib/pages/pickup_search/pickup_search.dart +++ b/lib/pages/pickup_search/pickup_search.dart @@ -9,6 +9,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:provider/provider.dart'; +import '../../helpers/shared_pref.dart'; +import '../widgets/suggest_list.dart'; + Future searchPickup(BuildContext context, {CallbackPickupSelect? callbackPickupSelect}) async => await showSearch( @@ -32,7 +35,7 @@ class PackageSearchDelegate extends SearchDelegate { appBarTheme: AppBarTheme(color: primaryColor), iconButtonTheme: IconButtonThemeData( style: ButtonStyle( - iconColor: MaterialStateProperty.all(Colors.white))), + iconColor: WidgetStateProperty.all(Colors.white))), inputDecorationTheme: InputDecorationTheme( border: InputBorder.none, hintStyle: TextStyle(color: Colors.grey, fontSize: 14)), @@ -124,13 +127,27 @@ class PackageSearchDelegate extends SearchDelegate { @override Widget buildSuggestions(BuildContext context) { - return Container( - child: Center( - child: Opacity( - opacity: 0.2, - child: Icon(SimpleLineIcons.direction, - size: 200, color: primaryColor)), - ), + return FutureBuilder?>( + future: SharedPref.getRecentSearch('pickup_search', query), + builder: (context, snapshot) { + List _oldFilters = snapshot.data ?? []; + if (_oldFilters.isEmpty) { + return const Center( + child: Opacity( + opacity: 0.2, + child: Icon(SimpleLineIcons.direction, + color: primaryColor, size: 200), + ), + ); + } + return SuggestList( + recentSearchList: _oldFilters, + onTap: (String s) { + query = s; + showResults(context); + }, + prefKey: 'pickup_search'); + }, ); } diff --git a/lib/pages/rates/cargo_editor.dart b/lib/pages/rates/cargo_editor.dart index 51a5407..2ffa1a5 100644 --- a/lib/pages/rates/cargo_editor.dart +++ b/lib/pages/rates/cargo_editor.dart @@ -4,9 +4,11 @@ import 'package:fcs/pages/main/util.dart'; import 'package:fcs/pages/widgets/input_text.dart'; import 'package:fcs/pages/widgets/local_app_bar.dart'; import 'package:fcs/pages/widgets/progress.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../widgets/local_text.dart'; import 'model/shipment_rate_model.dart'; class CargoEditor extends StatefulWidget { @@ -20,11 +22,13 @@ class CargoEditor extends StatefulWidget { class _CargoEditorState extends State { TextEditingController _descController = new TextEditingController(); TextEditingController _rateController = new TextEditingController(); + TextEditingController _displayIndexController = new TextEditingController(); bool _isLoading = false; late CargoType _cargo; bool _isNew = false; final _cargoFormKey = GlobalKey(); + bool _isDefault = false; @override void initState() { @@ -33,6 +37,8 @@ class _CargoEditorState extends State { _cargo = widget.cargo!; _descController.text = _cargo.name ?? ""; _rateController.text = _cargo.rate.toStringAsFixed(2); + _displayIndexController.text = _cargo.displayIndex.toString(); + _isDefault = _cargo.isDefault; } else { _isNew = true; } @@ -46,20 +52,23 @@ class _CargoEditorState extends State { @override Widget build(BuildContext context) { final typeBox = InputText( - labelTextKey: 'cargo.type', - iconData: Icons.text_format, - controller: _descController, - autovalidateMode: AutovalidateMode.onUserInteraction, - validator: (value){ - if(value==null || value.isEmpty){ - return "Please insert cargo type"; - } - return null; - },); + labelTextKey: 'cargo.type', + iconData: Icons.text_format, + controller: _descController, + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: (value) { + if (value == null || value.isEmpty) { + return "Please insert cargo type"; + } + return null; + }, + ); + final rateBox = InputText( labelTextKey: 'cargo.rate', iconData: Icons.attach_money, controller: _rateController, + textInputType: TextInputType.number, autovalidateMode: AutovalidateMode.onUserInteraction, validator: (value) { if (value == null || value.isEmpty) { @@ -68,6 +77,51 @@ class _CargoEditorState extends State { return null; }, ); + + final displayIndexBox = InputText( + labelTextKey: 'cargo.display_index', + iconData: Icons.numbers, + controller: _displayIndexController, + textInputType: TextInputType.number, + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: (value) { + if (value == null || value.isEmpty) { + return "Please insert display index"; + } + return null; + }, + ); + + final defaultBox = Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocalText( + context, + 'cargo.is_default', + color: Colors.black54, + fontSize: 15, + ), + ], + ), + ), + Transform.scale( + scale: 0.7, + alignment: Alignment.centerRight, + child: CupertinoSwitch( + activeColor: primaryColor, + value: _isDefault, + onChanged: (v) { + setState(() { + _isDefault = v; + }); + })) + ]), + ); + return LocalProgress( inAsyncCall: _isLoading, child: Scaffold( @@ -105,6 +159,8 @@ class _CargoEditorState extends State { children: [ typeBox, rateBox, + displayIndexBox, + defaultBox, SizedBox(height: 30), ], ), @@ -131,7 +187,10 @@ class _CargoEditorState extends State { var shipmentRateModel = Provider.of(context, listen: false); CargoType _cargo = CargoType( - name: _descController.text, rate: double.parse(_rateController.text)); + name: _descController.text, + rate: double.parse(_rateController.text), + displayIndex: int.parse(_displayIndexController.text), + isDefault: _isDefault); if (_isNew) { await shipmentRateModel.addCargoType(_cargo); } else { diff --git a/lib/pages/user_search/user_search.dart b/lib/pages/user_search/user_search.dart index 76dff2b..2a86d51 100644 --- a/lib/pages/user_search/user_search.dart +++ b/lib/pages/user_search/user_search.dart @@ -37,7 +37,7 @@ class UserSearchDelegate extends SearchDelegate { appBarTheme: AppBarTheme(color: primaryColor), iconButtonTheme: IconButtonThemeData( style: ButtonStyle( - iconColor: MaterialStateProperty.all(Colors.white))), + iconColor: WidgetStateProperty.all(Colors.white))), inputDecorationTheme: InputDecorationTheme( border: InputBorder.none, hintStyle: TextStyle(color: Colors.grey, fontSize: 14)), diff --git a/lib/pagination/paginator_listview.dart b/lib/pagination/paginator_listview.dart index 8a3f087..ab906cc 100644 --- a/lib/pagination/paginator_listview.dart +++ b/lib/pagination/paginator_listview.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; import '../helpers/theme.dart'; import 'paginator_listener.dart'; @@ -99,7 +100,23 @@ class _PaginatorListViewState extends State> { ); } T t = _paginatorListener.data[index]; - return widget.rowBuilder(t); + return Slidable( + enabled: widget.onRemove != null ? true : false, + endActionPane: ActionPane( + extentRatio: 0.25, + motion: const ScrollMotion(), + children: widget.onRemove != null + ? [ + SlidableAction( + onPressed: ((context) => widget.onRemove!(t)), + backgroundColor: Colors.red, + icon: Icons.delete, + label: 'Delete', + ), + ] + : [], + ), + child: widget.rowBuilder(t)); }), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index 8f43e1b..3c1b16c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,6 +58,7 @@ dependencies: pdf: ^3.10.8 qr_flutter: ^4.1.0 flutter_markdown: ^0.6.20+1 + flutter_slidable: ^3.1.1 dev_dependencies: flutter_test: From 41fdc3ef43b289de5b9753195ff8a30739700ded Mon Sep 17 00:00:00 2001 From: tzw Date: Tue, 1 Oct 2024 18:15:53 +0630 Subject: [PATCH 3/5] update carton form ,info and filter --- lib/data/provider/carton_data_provider.dart | 2 - lib/domain/entities/cargo_type.dart | 5 +- lib/main-dev.dart | 6 +- lib/pages/carton/carton_info.dart | 111 +++++++++--------- lib/pages/carton/carton_package_editor.dart | 24 ++-- lib/pages/carton/carton_size_widget.dart | 6 +- lib/pages/carton/carton_submit.dart | 2 +- .../carton/mix_carton/mix_carton_editor.dart | 5 +- .../carton/mix_carton/mix_carton_submit.dart | 35 +++--- lib/pages/carton/model/carton_model.dart | 12 +- .../model/consignee_selection_model.dart | 4 +- .../carton/model/sender_selection_model.dart | 4 +- lib/pages/rates/cargo_editor.dart | 16 +-- 13 files changed, 123 insertions(+), 109 deletions(-) diff --git a/lib/data/provider/carton_data_provider.dart b/lib/data/provider/carton_data_provider.dart index c4de1cb..1b515d1 100644 --- a/lib/data/provider/carton_data_provider.dart +++ b/lib/data/provider/carton_data_provider.dart @@ -15,7 +15,6 @@ class CartonDataProvider { Future createCarton(Carton carton) async { var data = await requestAPI("/cartons", "POST", payload: carton.toMap(), token: await getToken()); - print("carton data:${data}"); return Carton.fromMap(data, data['id']); } @@ -32,7 +31,6 @@ class CartonDataProvider { Future createMixCarton(Carton carton) async { var data = await requestAPI("/cartons", "POST", payload: carton.toMapForMix(), token: await getToken()); - print("carton mix data:${data}"); return Carton.fromMap(data, data['id']); } diff --git a/lib/domain/entities/cargo_type.dart b/lib/domain/entities/cargo_type.dart index b435209..3b64cc2 100644 --- a/lib/domain/entities/cargo_type.dart +++ b/lib/domain/entities/cargo_type.dart @@ -44,7 +44,10 @@ class CargoType { factory CargoType.fromMapForCargo(Map map, String id) { return CargoType( - id: id, name: map['name'], weight: map['weight']?.toDouble() ?? 0); + id: id, + name: map['name'], + weight: map['weight']?.toDouble() ?? 0, + displayIndex: map['display_index'] ?? 0); } factory CargoType.fromMapForsurcharge(Map map, String id) { diff --git a/lib/main-dev.dart b/lib/main-dev.dart index 850c75a..def8841 100644 --- a/lib/main-dev.dart +++ b/lib/main-dev.dart @@ -11,15 +11,15 @@ import 'data/provider/messaging_fcm.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( - options: DefaultFirebaseOptions.currentPlatform, -); + options: DefaultFirebaseOptions.currentPlatform, + ); FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); Config( flavor: Flavor.DEV, color: Colors.blue, - apiURL:"http://192.168.100.150:9090", + apiURL: "http://192.168.100.150:9090", // apiURL: "https://asia-northeast1-fcs-dev1.cloudfunctions.net/API13", reportURL: "http://petrok.mokkon.com:7071", reportProjectID: "fcs-dev", diff --git a/lib/pages/carton/carton_info.dart b/lib/pages/carton/carton_info.dart index be1fec1..873e754 100644 --- a/lib/pages/carton/carton_info.dart +++ b/lib/pages/carton/carton_info.dart @@ -92,6 +92,8 @@ class _CartonInfoState extends State { _mixCartons = await context .read() .getCartonsByIds(_carton.cartonIDs); + _cargoTypes.sort((a, b) => a.name!.compareTo(b.name!)); + _surchareItems.sort((a, b) => a.name!.compareTo(b.name!)); } totalWeight = @@ -156,13 +158,17 @@ class _CartonInfoState extends State { ); final lastMileBox = DisplayText( - text: _carton.lastMile == delivery_caton ? 'Delivery' : 'Pick-up', + text: _carton.lastMile == delivery_caton + ? 'Delivery' + : _carton.lastMile == pickup_carton + ? 'Pick-up' + : '', labelTextKey: "box.delivery_type", ); final cartonSizeBox = DisplayText( subText: Text("${boxDimension ?? 'No defined size'}"), - labelTextKey: "box.carton_size", + labelTextKey: "box.select_carton_size", ); final senderBox = DisplayText( @@ -192,6 +198,7 @@ class _CartonInfoState extends State { final userRowBox = Row( mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Row( @@ -228,7 +235,7 @@ class _CartonInfoState extends State { ); final cargosBox = Padding( - padding: const EdgeInsets.only(top: 15), + padding: const EdgeInsets.only(top: 20), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -237,10 +244,13 @@ class _CartonInfoState extends State { color: Colors.black54, fontSize: 16, fontWeight: FontWeight.normal), - Padding( - padding: EdgeInsets.only(right: 50), - child: Text("${removeTrailingZeros(totalWeight)} lb", - style: TextStyle(color: Colors.black54, fontSize: 15))) + _cargoTypes.isNotEmpty + ? Padding( + padding: EdgeInsets.only(right: 50), + child: Text("${removeTrailingZeros(totalWeight)} lb", + textAlign: TextAlign.end, + style: TextStyle(color: Colors.black54, fontSize: 15))) + : const SizedBox() ], ), Container( @@ -262,68 +272,52 @@ class _CartonInfoState extends State { TextStyle(color: Colors.black, fontSize: 15), ), Text("${removeTrailingZeros(e.weight)} lb", + textAlign: TextAlign.end, style: TextStyle( color: Colors.black, fontSize: 15)) ], ), ); }).toList()), - const SizedBox(height: 10), ], ), ), ), ]), ); - final surchargeItemBox = Padding( - padding: const EdgeInsets.only(top: 15), - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - LocalText(context, 'box.surcharge.item', - color: Colors.black54, - fontSize: 16, - fontWeight: FontWeight.normal), - Padding( - padding: EdgeInsets.only(right: 50), - child: Text("${removeTrailingZeros(totalSurchargeCount)} pcs", - style: TextStyle(color: Colors.black54, fontSize: 15))) - ], - ), - Container( - child: Padding( - padding: const EdgeInsets.only(right: 60), - child: Column( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: _surchareItems.map((e) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 3), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - e.name ?? "", + + final surchargeItemBox = + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Container( + child: Padding( + padding: const EdgeInsets.only(right: 50), + child: Column( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _surchareItems.map((e) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 3), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + e.name ?? "", + style: TextStyle(color: Colors.black, fontSize: 15), + ), + Text("${removeTrailingZeros((e.qty).toDouble())} pc", + textAlign: TextAlign.end, style: - TextStyle(color: Colors.black, fontSize: 15), - ), - Text( - "${removeTrailingZeros((e.qty).toDouble())} pc", - style: TextStyle( - color: Colors.black, fontSize: 15)) - ], - ), - ); - }).toList()), - const SizedBox(height: 10), - ], - ), + TextStyle(color: Colors.black, fontSize: 15)) + ], + ), + ); + }).toList()), + ], ), ), - ]), - ); + ), + ]); final img = MultiImageFile( enabled: false, @@ -447,10 +441,13 @@ class _CartonInfoState extends State { ], ), ), - _cargoTypes.isEmpty ? const SizedBox() : cargosBox, - _surchareItems.isEmpty ? const SizedBox() : surchargeItemBox, + cargosBox, + surchargeItemBox, + const SizedBox(height: 10), uploadImageBtn, - const SizedBox(height: 30), + _carton.photoUrls.isNotEmpty + ? const SizedBox(height: 10) + : const SizedBox(), img, _carton.photoUrls.isNotEmpty ? const SizedBox(height: 40) diff --git a/lib/pages/carton/carton_package_editor.dart b/lib/pages/carton/carton_package_editor.dart index 5fe0ca5..baeabee 100644 --- a/lib/pages/carton/carton_package_editor.dart +++ b/lib/pages/carton/carton_package_editor.dart @@ -89,15 +89,6 @@ class _CartonPackageEditorState extends State { _cargoTypes = widget.carton.cargoTypes; _surchareItems = widget.carton.surchareItems; - var s = await context - .read() - .getFcsShipment(widget.carton.fcsShipmentID ?? ""); - _shipment = s; - - _packages = await context - .read() - .getPackagesByIds(widget.carton.packageIDs); - // check carton size type List cartonSizes = context.read().cartonSizes; @@ -120,8 +111,20 @@ class _CartonPackageEditorState extends State { _cartonSizeType = packageCarton; } else { _cartonSizeType = customCarton; + _length = widget.carton.length.toDouble(); + _width = widget.carton.width.toDouble(); + _height = widget.carton.height.toDouble(); } + var s = await context + .read() + .getFcsShipment(widget.carton.fcsShipmentID ?? ""); + _shipment = s; + + _packages = await context + .read() + .getPackagesByIds(widget.carton.packageIDs); + if (mounted) { setState(() {}); } @@ -328,7 +331,10 @@ class _CartonPackageEditorState extends State { } var carton = Carton( + id: widget.carton.id, cartonType: carton_from_packages, + senderID: widget.carton.senderID, + consigneeID: widget.carton.consigneeID, billTo: _billToValue, lastMile: _selectedLastMile, fcsShipmentID: _shipment?.id, diff --git a/lib/pages/carton/carton_size_widget.dart b/lib/pages/carton/carton_size_widget.dart index 9beaa4b..09916a3 100644 --- a/lib/pages/carton/carton_size_widget.dart +++ b/lib/pages/carton/carton_size_widget.dart @@ -61,8 +61,6 @@ class CartonSizeWidget extends StatefulWidget { } class _CartonSizeWidgetState extends State { - // List _deliveryTypes = [delivery_caton, pickup_carton]; - FcsShipment? _shipment; String _cartonSizeType = standardCarton; @@ -85,7 +83,6 @@ class _CartonSizeWidgetState extends State { _selectedLastmile = widget.lastMile; _billToValue = widget.billType; _cartonSizeType = widget.cartonSizeType; - _lengthController.text = widget.length == null ? "0" : removeTrailingZeros(widget.length ?? 0); _widthController.text = @@ -94,8 +91,7 @@ class _CartonSizeWidgetState extends State { widget.height == null ? "0" : removeTrailingZeros(widget.height ?? 0); _getStandardCartonSize(); - _loadShipment(); - + _loadShipment(); if (mounted) { setState(() {}); } diff --git a/lib/pages/carton/carton_submit.dart b/lib/pages/carton/carton_submit.dart index 804dff8..a65db3a 100644 --- a/lib/pages/carton/carton_submit.dart +++ b/lib/pages/carton/carton_submit.dart @@ -191,7 +191,7 @@ class CartonSubmit extends StatelessWidget { final cartonSizeBox = Padding( padding: const EdgeInsets.only(top: 10), child: SubmitTextWidget( - labelKey: 'box.carton_size', + labelKey: 'box.select_carton_size', text: boxDimension ?? 'No defined size', ), ); diff --git a/lib/pages/carton/mix_carton/mix_carton_editor.dart b/lib/pages/carton/mix_carton/mix_carton_editor.dart index 26b6b00..2b9c6ce 100644 --- a/lib/pages/carton/mix_carton/mix_carton_editor.dart +++ b/lib/pages/carton/mix_carton/mix_carton_editor.dart @@ -82,6 +82,9 @@ class _MixCartonEditorState extends State { _cartonSizeType = packageCarton; } else { _cartonSizeType = customCarton; + _length = widget.carton.length.toDouble(); + _width = widget.carton.width.toDouble(); + _height = widget.carton.height.toDouble(); } var s = await context @@ -269,7 +272,7 @@ class _MixCartonEditorState extends State { width: width, height: height, cartons: _cartons); - await context.read().updateCarton(carton); + await context.read().updateMixCarton(carton); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); diff --git a/lib/pages/carton/mix_carton/mix_carton_submit.dart b/lib/pages/carton/mix_carton/mix_carton_submit.dart index 52e164d..6da9ce5 100644 --- a/lib/pages/carton/mix_carton/mix_carton_submit.dart +++ b/lib/pages/carton/mix_carton/mix_carton_submit.dart @@ -48,8 +48,8 @@ class MixCartonSubmit extends StatefulWidget { class _MixCartonSubmitState extends State { final NumberFormat numberFormatter = NumberFormat("#,###"); - Map _mapCargosByWeight = {}; - Map _mapCargosByCustomDutyFee = {}; + Map _mapCargos = {}; + Map _mapSurchargeItems = {}; double totalWeight = 0; @override @@ -59,30 +59,37 @@ class _MixCartonSubmitState extends State { } _init() { + // get cargos by weight List _cargoTypes = []; for (var c in widget.cartons) { _cargoTypes.addAll(c.cargoTypes); } - // get cargos by weight - Map> _cargosByWeight = - groupCargos(_cargoTypes.where((e) => !e.isCutomDuty).toList()); + _cargoTypes.sort((a, b) => a.name!.compareTo(b.name!)); + Map> _cargosByWeight = groupCargos(_cargoTypes); _cargosByWeight.forEach((key, value) { double total = value.fold(0, (sum, item) => sum + item.weight); - _mapCargosByWeight[key] = total; + _mapCargos[key] = total; }); - totalWeight = - _mapCargosByWeight.entries.fold(0, (sum, value) => sum + value.value); + totalWeight = _mapCargos.entries.fold(0, (sum, value) => sum + value.value); + + // get surcharge items + + List _surchargeItems = []; + for (var c in widget.cartons) { + _surchargeItems.addAll(c.surchareItems); + } + + _surchargeItems.sort((a, b) => a.name!.compareTo(b.name!)); - // get cargos by custom duty fee Map> _cargosByCustomDutyFee = - groupCargos(_cargoTypes.where((e) => e.isCutomDuty).toList()); + groupCargos(_surchargeItems); _cargosByCustomDutyFee.forEach((key, value) { double total = value.fold(0, (sum, item) => sum + item.qty); - _mapCargosByCustomDutyFee[key] = total; + _mapSurchargeItems[key] = total; }); if (mounted) { @@ -116,7 +123,7 @@ class _MixCartonSubmitState extends State { final cartonSizeBox = Padding( padding: const EdgeInsets.only(top: 10), child: SubmitTextWidget( - labelKey: 'box.carton_size', + labelKey: 'box.select_carton_size', text: boxDimension ?? 'No defined size', ), ); @@ -195,7 +202,7 @@ class _MixCartonSubmitState extends State { children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, - children: _mapCargosByWeight.entries.map((e) { + children: _mapCargos.entries.map((e) { return Padding( padding: const EdgeInsets.symmetric(vertical: 3), child: Row( @@ -216,7 +223,7 @@ class _MixCartonSubmitState extends State { const SizedBox(height: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, - children: _mapCargosByCustomDutyFee.entries.map((e) { + children: _mapSurchargeItems.entries.map((e) { return Padding( padding: const EdgeInsets.symmetric(vertical: 3), child: Row( diff --git a/lib/pages/carton/model/carton_model.dart b/lib/pages/carton/model/carton_model.dart index 49148c5..c8fe9f1 100644 --- a/lib/pages/carton/model/carton_model.dart +++ b/lib/pages/carton/model/carton_model.dart @@ -117,13 +117,15 @@ class CartonModel extends BaseModel { Query pageQuery = FirebaseFirestore.instance.collection(path); if (filterByConsingee != null) { - col = col.where("user_id", isEqualTo: filterByConsingee!.id); - pageQuery = pageQuery.where("user_id", isEqualTo: filterByConsingee!.id); + col = col.where("consignee_user_id", isEqualTo: filterByConsingee!.id); + pageQuery = pageQuery.where("consignee_user_id", + isEqualTo: filterByConsingee!.id); } if (filterBySender != null) { - col = col.where("sender_id", isEqualTo: filterBySender!.id); - pageQuery = pageQuery.where("sender_id", isEqualTo: filterBySender!.id); + col = col.where("sender_user_id", isEqualTo: filterBySender!.id); + pageQuery = + pageQuery.where("sender_user_id", isEqualTo: filterBySender!.id); } if (filterByStatus != null) { @@ -208,7 +210,7 @@ class CartonModel extends BaseModel { } Future createMixCarton(Carton carton) { - return Services.instance.cartonService.createCarton(carton); + return Services.instance.cartonService.createMixCarton(carton); } Future updateMixCarton(Carton carton) { diff --git a/lib/pages/carton/model/consignee_selection_model.dart b/lib/pages/carton/model/consignee_selection_model.dart index 0682e5c..7377de4 100644 --- a/lib/pages/carton/model/consignee_selection_model.dart +++ b/lib/pages/carton/model/consignee_selection_model.dart @@ -91,8 +91,8 @@ class ConsigneeSelectionModel extends BaseModel { Query query = FirebaseFirestore.instance .collection(path) .where("is_sys_admin", isEqualTo: false) - .where("is_deleted", isEqualTo: false) - .orderBy("message_time", descending: true); + .where("delete_time", isEqualTo: 0) + .orderBy("update_time", descending: true); if (_lastDocument != null) { query = query.startAfterDocument(_lastDocument!); diff --git a/lib/pages/carton/model/sender_selection_model.dart b/lib/pages/carton/model/sender_selection_model.dart index aced32e..8179099 100644 --- a/lib/pages/carton/model/sender_selection_model.dart +++ b/lib/pages/carton/model/sender_selection_model.dart @@ -93,8 +93,8 @@ class SenderSelectionModel extends BaseModel { Query query = FirebaseFirestore.instance .collection(path) .where("is_sys_admin", isEqualTo: false) - .where("is_deleted", isEqualTo: false) - .orderBy("message_time", descending: true); + .where("delete_time", isEqualTo: 0) + .orderBy("update_time", descending: true); if (_lastDocument != null) { query = query.startAfterDocument(_lastDocument!); diff --git a/lib/pages/rates/cargo_editor.dart b/lib/pages/rates/cargo_editor.dart index 2ffa1a5..6645e4c 100644 --- a/lib/pages/rates/cargo_editor.dart +++ b/lib/pages/rates/cargo_editor.dart @@ -84,12 +84,12 @@ class _CargoEditorState extends State { controller: _displayIndexController, textInputType: TextInputType.number, autovalidateMode: AutovalidateMode.onUserInteraction, - validator: (value) { - if (value == null || value.isEmpty) { - return "Please insert display index"; - } - return null; - }, + // validator: (value) { + // if (value == null || value.isEmpty) { + // return "Please insert display index"; + // } + // return null; + // }, ); final defaultBox = Padding( @@ -189,7 +189,9 @@ class _CargoEditorState extends State { CargoType _cargo = CargoType( name: _descController.text, rate: double.parse(_rateController.text), - displayIndex: int.parse(_displayIndexController.text), + displayIndex: _displayIndexController.text == '' + ? 0 + : int.parse(_displayIndexController.text), isDefault: _isDefault); if (_isNew) { await shipmentRateModel.addCargoType(_cargo); From 81dfeb037db352359950016c2f493752426ffa3b Mon Sep 17 00:00:00 2001 From: tzw Date: Fri, 4 Oct 2024 13:55:59 +0630 Subject: [PATCH 4/5] add pin login and add pin code --- assets/local/localization_en.json | 2 + assets/local/localization_mu.json | 2 + lib/app.dart | 2 + lib/constants.dart | 1 + lib/data/provider/auth_fb.dart | 82 +++++++++++-- lib/data/services/auth_imp.dart | 19 +++ lib/data/services/auth_service.dart | 6 + lib/domain/entities/user.dart | 9 +- lib/domain/vo/privilege.dart | 3 + lib/helpers/api_helper.dart | 6 + lib/helpers/shared_pref.dart | 10 ++ lib/pages/main/home_page.dart | 48 ++++++-- lib/pages/main/model/main_model.dart | 26 ++++ lib/pages/main/splash_page.dart | 6 +- lib/pages/profile/profile_page.dart | 25 ++-- lib/pages/signin/pinlogin_page.dart | 157 ++++++++++++++++++------- lib/pages/staff/model/staff_model.dart | 2 +- lib/pages/staff/staff_pin_editor.dart | 2 +- 18 files changed, 340 insertions(+), 68 deletions(-) diff --git a/assets/local/localization_en.json b/assets/local/localization_en.json index 0c3a1e9..74fa593 100644 --- a/assets/local/localization_en.json +++ b/assets/local/localization_en.json @@ -18,6 +18,7 @@ "back.button_confirm":"Are you sure you want to continue without submitting changes?", "btn.clear":"Clear Filter", "btn.filter":"Filter", + "btn.exit_confirm":"Are you sure you want to exit?", "Buttons End ================================================================":"", "Offline Start ================================================================":"", @@ -104,6 +105,7 @@ "home.invitation.request.msg":"We are working on your invitation request!", "home.search":"Enter tracking number", "home.search.btn":"Search", + "home.pin.logout.confirm":"Are you sure want to logout?", "Home End ================================================================":"", "Invite Start ================================================================":"", diff --git a/assets/local/localization_mu.json b/assets/local/localization_mu.json index 02666f6..befe69c 100644 --- a/assets/local/localization_mu.json +++ b/assets/local/localization_mu.json @@ -17,6 +17,7 @@ "back.button_confirm":"Are you sure you want to continue without submitting changes?", "btn.clear":"Clear Filter", "btn.filter":"Filter", + "btn.exit_confirm":"Are you sure you want to exit?", "Buttons End ================================================================":"", "Offline Start ================================================================":"", @@ -104,6 +105,7 @@ "home.invitation.request.msg":"ဖိတ်ကြားမှု တောင်းဆိုသည်ကို လုပ်ဆောင်နေပါသည်!", "home.search":"Tracking number ရိုက်ထည့်ပါ", "home.search.btn":"ရှာမည်", + "home.pin.logout.confirm":"အကောင့်ထွက်ရန်သေချာပြီလား?", "Home End ================================================================":"", "Invite Start ================================================================":"", diff --git a/lib/app.dart b/lib/app.dart index d3243fa..779f703 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -35,6 +35,7 @@ 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'; +import 'pages/signin/pinlogin_page.dart'; class App extends StatefulWidget { final String title; @@ -121,6 +122,7 @@ class _AppState extends State { '/welcome': (_) => WelcomePage(), '/home': (_) => HomePage(), '/language_selection': (context) => InitialLanguageSelectionPage(), + '/pin_login': (_) => PinLoginPage() }; return routes; } diff --git a/lib/constants.dart b/lib/constants.dart index 53f1ba7..5592bcb 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -83,6 +83,7 @@ const privilege_receiving = "rc"; const privilege_pickup = "pku"; const privilege_collect = "col"; const privilege_report = "rpt"; +const privilege_pin ="pin"; // Pickup types const shipment_local_pickup = "Local pickup"; diff --git a/lib/data/provider/auth_fb.dart b/lib/data/provider/auth_fb.dart index cd603e8..c55da89 100644 --- a/lib/data/provider/auth_fb.dart +++ b/lib/data/provider/auth_fb.dart @@ -12,6 +12,7 @@ import 'package:fcs/helpers/firebase_helper.dart'; import 'package:firebase_auth/firebase_auth.dart' as fb; import 'package:logging/logging.dart'; +import '../../helpers/shared_pref.dart'; import '../services/services.dart'; class AuthFb { @@ -108,13 +109,15 @@ class AuthFb { Future signoutStart() async { await userListener?.cancel(); await userAuthListener?.cancel(); + await _pinUserListener?.cancel(); } Future signoutEnd() async { + await SharedPref.setPinLockOn(false); await _fb.signOut(); } - Future _addUserToStream({bool refreshIdToken = false}) async { + Future _addUserToStream({bool refreshIdToken = false}) async { fb.User? firebaseUser = _fb.currentUser; if (firebaseUser == null) return null; Map? claims = @@ -128,15 +131,20 @@ class AuthFb { user = await _getUserFromFirestore(cid); } if (user == null) { - controller.add(null); - return; + _addUser(null); + return null; } loadUserClaim(claims, user); - controller.add(user); + _addUser(user); + return user; } loadUserClaim(Map claims, User user) { + if (pinLoginUser != null) { + user.fcsID = pinLoginUser?.fcsID; + } + // add privileges String? privileges = claims["pr"]; if (privileges != null && privileges != "") { @@ -252,9 +260,9 @@ class AuthFb { // get privilege from claim Map claims = await getClaims(refreshIdToken: true); loadUserClaim(claims, user); - controller.add(user); + _addUser(user); } catch (e) { - controller.add(null); + _addUser(null); } }); } @@ -286,12 +294,23 @@ class AuthFb { log.info("_startAuthListener: $user"); if (_logIn) { - controller.add(user); + _addUser(user); } } }); } + _addUser(User? user) { + if (user == null) { + pinLoginUser = null; + } + if (pinLoginUser != null) { + controller.add(pinLoginUser); + } else { + controller.add(user); + } + } + Stream user() { // ignore: close_sinks StreamSubscription? authListener; @@ -301,7 +320,7 @@ class AuthFb { authListener = _fb.authStateChanges().listen((firebaseUser) async { _logIn = firebaseUser != null; if (firebaseUser == null) { - controller.add(null); + _addUser(null); } else { _addUserToStream(refreshIdToken: true); _startUserListener(); @@ -319,4 +338,51 @@ class AuthFb { return controller.stream; } + + User? pinLoginUser; + + StreamSubscription? _pinUserListener; + + Future pinLogin( + {required String currentUserId, + required String fcsID, + required String pin}) async { + var data = await requestAPI("/pin/login", "POST", + token: await getToken(), payload: {'fcs_id': fcsID, 'pin': pin}); + + var userId = data['user_id'] ?? ''; + if (userId == currentUserId) { + //logout + logoutPinAccount(); + await SharedPref.setPinLockOn(false); + } + + var pinToken = data['pin_token'] ?? ''; + + // Get user for pin login + _listenPinUser(userId: userId, pinToken: pinToken); + } + + Future _listenPinUser( + {required String userId, required String pinToken}) async { + Stream snapshot = FirebaseFirestore.instance + .collection(user_collection) + .doc(userId) + .snapshots(); + _pinUserListener?.cancel(); + _pinUserListener = snapshot.listen((snap) async { + User user = User.fromMap(snap.data() as Map, snap.id); + pinLoginUser = user; + if (pinLoginUser != null) { + pinLoginUser!.pinToken = pinToken; + } + await _addUserToStream(refreshIdToken: true); + }); + } + + Future logoutPinAccount() async { + _pinUserListener?.cancel(); + pinLoginUser = null; + await _addUserToStream(refreshIdToken: true); + } } diff --git a/lib/data/services/auth_imp.dart b/lib/data/services/auth_imp.dart index def1606..a923101 100644 --- a/lib/data/services/auth_imp.dart +++ b/lib/data/services/auth_imp.dart @@ -74,4 +74,23 @@ class AuthServiceImp implements AuthService { Future deleteAccount() { return authFb.deleteAccount(); } + + @override + Future pinLogin( + {required String currentUserId, + required String fcsID, + required String pin}) { + return authFb.pinLogin( + currentUserId: currentUserId, fcsID: fcsID, pin: pin); + } + + @override + User? getPinLoginUser() { + return authFb.pinLoginUser; + } + + @override + Future logoutPinAccount() { + return authFb.logoutPinAccount(); + } } diff --git a/lib/data/services/auth_service.dart b/lib/data/services/auth_service.dart index 4a6c6ad..90017a3 100644 --- a/lib/data/services/auth_service.dart +++ b/lib/data/services/auth_service.dart @@ -15,4 +15,10 @@ abstract class AuthService { Stream getUserStream(); Stream getSetting(); Future deleteAccount(); + Future pinLogin( + {required String currentUserId, + required String fcsID, + required String pin}); + User? getPinLoginUser(); + Future logoutPinAccount(); } diff --git a/lib/domain/entities/user.dart b/lib/domain/entities/user.dart index bef8dec..6751e72 100644 --- a/lib/domain/entities/user.dart +++ b/lib/domain/entities/user.dart @@ -21,6 +21,7 @@ class User { String? preferCurrency; bool enablePinLogin; String? pinDigit; + List privileges = []; String get initial => name != null && name != "" ? name!.substring(0, 1) : "?"; @@ -48,7 +49,9 @@ class User { String get getFcsUnseenCount => fcsUnseenCount > 100 ? "99+" : fcsUnseenCount.toString(); - List privileges = []; + // for pin login + String? pinToken; + bool get isPinLogin => pinToken!=null; String get phone => phoneNumber != null && phoneNumber!.startsWith("959") ? "0${phoneNumber!.substring(2)}" @@ -117,7 +120,9 @@ class User { userUnseenCount: map['user_unseen_count'] ?? 0, fcsUnseenCount: map['fcs_unseen_count'] ?? 0, preferCurrency: map['preferred_currency'], - lastMessageTime: _date == null ? null : _date.toDate()); + lastMessageTime: _date == null ? null : _date.toDate(), + enablePinLogin: map['enable_pin_login'] ?? false, + pinDigit: map['pin'] ?? ''); } bool diffPrivileges(User another) { diff --git a/lib/domain/vo/privilege.dart b/lib/domain/vo/privilege.dart index afec5fb..f170f57 100644 --- a/lib/domain/vo/privilege.dart +++ b/lib/domain/vo/privilege.dart @@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:flutter_vector_icons/flutter_vector_icons.dart' as vector; class Privilege { String id; @@ -49,6 +50,8 @@ class Privilege { iconData = MaterialCommunityIcons.layers; } else if (this.id == privilege_report) { iconData = Feather.file_text; + } else if (this.id == privilege_pin) { + iconData = vector.MaterialCommunityIcons.account_lock_outline; } else { iconData = MaterialCommunityIcons.account_question; } diff --git a/lib/helpers/api_helper.dart b/lib/helpers/api_helper.dart index 240a9df..81fa7e5 100644 --- a/lib/helpers/api_helper.dart +++ b/lib/helpers/api_helper.dart @@ -4,6 +4,8 @@ import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:dio/dio.dart'; +import 'package:fcs/data/services/services.dart'; +import 'package:fcs/domain/entities/user.dart'; import 'package:fcs/domain/vo/status.dart'; import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; @@ -28,6 +30,10 @@ Future requestAPI(String path, method, if (token != null) { headers["Token"] = token; } + User? pinLoginUser = Services.instance.authService.getPinLoginUser(); + if (pinLoginUser != null) { + headers["pin_token"] = pinLoginUser.pinToken; + } if (devInfo.deviceID != null) { headers["Device"] = devInfo.deviceID ?? "" + ":" + deviceName; } diff --git a/lib/helpers/shared_pref.dart b/lib/helpers/shared_pref.dart index 68d1921..3ba06bd 100644 --- a/lib/helpers/shared_pref.dart +++ b/lib/helpers/shared_pref.dart @@ -108,4 +108,14 @@ class SharedPref { static Future clearRecentSearch(String key) async { return await _remove(key); } + + static Future getPinLockOn() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.getBool('pin_lock'); + } + + static Future setPinLockOn(bool isLockOn) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setBool('pin_lock', isLockOn); + } } diff --git a/lib/pages/main/home_page.dart b/lib/pages/main/home_page.dart index c1ad000..f04d77d 100644 --- a/lib/pages/main/home_page.dart +++ b/lib/pages/main/home_page.dart @@ -203,9 +203,27 @@ class _HomePageState extends State { super.dispose(); } + _logoutPinAccount() async { + setState(() { + _isLoading = true; + }); + + try { + await context.read().logoutPinAccount(); + Navigator.pushNamedAndRemoveUntil(context, "/pin_login", (r) => false); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } + @override Widget build(BuildContext context) { - User? user = Provider.of(context).user; + var mainModel = context.watch(); + User? user = mainModel.user; if (user == null) { Future.microtask( @@ -213,7 +231,7 @@ class _HomePageState extends State { return Container(); } - login = Provider.of(context).isLogin(); + login = mainModel.isLogin(); LanguageModel languageModel = Provider.of(context); final faqBtn = TaskButton("faq.btn", @@ -357,7 +375,7 @@ class _HomePageState extends State { selectedColor: Colors.white, color: Colors.blue, children: [ - Icon(MaterialCommunityIcons.account_tie,size: 25), + Icon(MaterialCommunityIcons.account_tie, size: 25), ], onPressed: (i) => this.setState(() { isFcs[0] = !isFcs[0]; @@ -387,11 +405,14 @@ class _HomePageState extends State { color: buttonColor, ), ); - + final pinLoginBtn = IconButton( onPressed: () { - Navigator.of(context) - .push(CupertinoPageRoute(builder: (context) => PinLoginPage())); + Navigator.pushAndRemoveUntil( + context, + CupertinoPageRoute( + builder: (BuildContext context) => PinLoginPage()), + (r) => false); }, iconSize: 25, icon: Icon( @@ -400,6 +421,19 @@ class _HomePageState extends State { ), ); + final pinLogoutBtn = IconButton( + onPressed: () { + showConfirmDialog(context, "home.pin.logout.confirm", () async { + await _logoutPinAccount(); + }); + }, + iconSize: 25, + icon: Icon( + MaterialCommunityIcons.lock_open_variant_outline, + color: buttonColor, + ), + ); + var searchInput = Row(children: [ Expanded( child: Padding( @@ -478,7 +512,7 @@ class _HomePageState extends State { profileBtn, ] : [ - pinLoginBtn, + mainModel.isPinLogin ? pinLogoutBtn : pinLoginBtn, fcsToggle, profileBtn, ] diff --git a/lib/pages/main/model/main_model.dart b/lib/pages/main/model/main_model.dart index b541704..781241f 100644 --- a/lib/pages/main/model/main_model.dart +++ b/lib/pages/main/model/main_model.dart @@ -17,6 +17,7 @@ class MainModel extends ChangeNotifier { String? messagingToken; User? user; + User? _fbUser; PackageInfo? packageInfo; set setMessaginToken(token) { @@ -29,6 +30,9 @@ class MainModel extends ChangeNotifier { bool isLoaded = false; bool isOnline = false; bool isFirstLaunch = false; + bool isLockOn = false; + + bool get isPinLogin => user?.isPinLogin ?? false; MainModel() { NetworkConnectivity.instance.statusStream.listen((data) { @@ -83,6 +87,7 @@ class MainModel extends ChangeNotifier { await _listenSetting(); this.isFirstLaunch = await SharedPref.isFirstLaunch() ?? true; this.packageInfo = await PackageInfo.fromPlatform(); + this.isLockOn = await SharedPref.getPinLockOn() ?? false; userListener?.cancel(); userListener = @@ -91,6 +96,17 @@ class MainModel extends ChangeNotifier { bool diffPrivilege = _user != null && (user == null || user!.diffPrivileges(_user)); bool loggingOut = user != null && _user == null; + + if (_user != null) { + if (!_user.isPinLogin) { + _fbUser = _user; + } + } + + if ((_fbUser?.id == _user?.id)) { + _user?.pinToken = null; + } + user = _user; if (_user != null) { @@ -101,6 +117,7 @@ class MainModel extends ChangeNotifier { } } } + if (loggingOut) { for (final m in models) { m.logout(); @@ -196,4 +213,13 @@ class MainModel extends ChangeNotifier { Future deleteAccount() async { return await Services.instance.authService.deleteAccount(); } + + Future pinLogin({required String fcsID, required String pin}) async { + await Services.instance.authService + .pinLogin(fcsID: fcsID, pin: pin, currentUserId: _fbUser?.id ?? ''); + } + + Future logoutPinAccount() async { + await Services.instance.authService.logoutPinAccount(); + } } diff --git a/lib/pages/main/splash_page.dart b/lib/pages/main/splash_page.dart index e719e5b..4b4e410 100644 --- a/lib/pages/main/splash_page.dart +++ b/lib/pages/main/splash_page.dart @@ -53,7 +53,11 @@ class _SplashScreenState extends State { if (mainModel.isFirstLaunch) { page = "/language_selection"; } else if (mainModel.isLogin()) { - page = "/home"; + if (mainModel.isLockOn) { + page = "/pin_login"; + } else { + page = "/home"; + } } else { page = "/welcome"; } diff --git a/lib/pages/profile/profile_page.dart b/lib/pages/profile/profile_page.dart index 4a9811b..6c9eb07 100644 --- a/lib/pages/profile/profile_page.dart +++ b/lib/pages/profile/profile_page.dart @@ -129,12 +129,17 @@ class _ProfileState extends State { ], ); - final logoutbutton = fcsButton( - context, getLocalString(context, "profile.logout"), callack: () { - showConfirmDialog(context, "profile.logout.confirm", () async { - await _logout(); - }); - }, iconData: Icons.exit_to_app); + final logoutbutton = + fcsButton(context, getLocalString(context, "profile.logout"), + callack: mainModel.isPinLogin + ? null + : () { + showConfirmDialog(context, "profile.logout.confirm", + () async { + await _logout(); + }); + }, + iconData: Icons.exit_to_app); return LocalProgress( inAsyncCall: _isLoading, @@ -355,8 +360,12 @@ class _ProfileState extends State { } _showToast(String title) { - final ScaffoldMessengerState scaffold = - key.currentState as ScaffoldMessengerState; + ScaffoldMessengerState? scaffold = key.currentState; + + if (scaffold == null) { + scaffold = ScaffoldMessenger.of(context); + } + scaffold.showSnackBar( SnackBar( content: Text('copied "$title" data to clipboard'), diff --git a/lib/pages/signin/pinlogin_page.dart b/lib/pages/signin/pinlogin_page.dart index 14d9eb6..31b11c7 100644 --- a/lib/pages/signin/pinlogin_page.dart +++ b/lib/pages/signin/pinlogin_page.dart @@ -1,13 +1,18 @@ import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/main/model/language_model.dart'; +import 'package:fcs/pages/main/model/main_model.dart'; import 'package:fcs/pages/main/util.dart'; import 'package:fcs/pages/widgets/local_button.dart'; import 'package:fcs/pages/widgets/local_text.dart'; import 'package:fcs/pages/widgets/progress.dart'; import 'package:flutter/material.dart'; import 'package:pin_input_text_field/pin_input_text_field.dart'; +import 'package:provider/provider.dart'; + +import '../../helpers/shared_pref.dart'; +import '../../localization/app_translations.dart'; class PinLoginPage extends StatefulWidget { - //final User user; const PinLoginPage({super.key}); @override @@ -18,10 +23,77 @@ class _PinLoginPageState extends State { bool _isLoading = false; String pin = ""; String prefixText = "FCS-"; - //late User _user; + final _formKey = GlobalKey(); TextEditingController _fcsIdCtl = new TextEditingController(); + @override + void initState() { + _init(); + super.initState(); + } + + _init() async { + await SharedPref.setPinLockOn(true); + } + + Future _onWillPop() async { + // Show the confirmation dialog + return (await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Center( + child: + LocalText(context, 'btn.exit_confirm', color: primaryColor), + ), + content: Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.grey[300], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0))), + child: Text( + AppTranslations.of(context)!.text('btn.cancel'), + style: Provider.of(context).isEng + ? TextStyle(color: primaryColor) + : TextStyle( + fontFamily: 'Myanmar3', color: primaryColor), + ), + onPressed: () { + Navigator.of(context).pop(false); + }), + SizedBox( + width: 0, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0))), + child: Text(AppTranslations.of(context)!.text('btn.ok'), + style: Provider.of(context).isEng + ? TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold) + : TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontFamily: 'Myanmar3')), + onPressed: () { + Navigator.of(context).pop(true); + }) + ], + ), + ), + ), + )) ?? + false; + } + Widget build(BuildContext context) { final fcsIdBox = TextFormField( controller: _fcsIdCtl, @@ -48,7 +120,7 @@ class _PinLoginPageState extends State { prefixText, style: TextStyle(color: Colors.black), ), - prefixIconConstraints: BoxConstraints(minWidth: 0, minHeight: 0), + prefixIconConstraints: BoxConstraints(minWidth: 0, minHeight: 0), contentPadding: EdgeInsets.all(0), labelStyle: newLabelStyle(color: Colors.black54, fontSize: 17), enabledBorder: UnderlineInputBorder( @@ -121,42 +193,47 @@ class _PinLoginPageState extends State { callBack: _login, ), ); - return LocalProgress( - inAsyncCall: _isLoading, - child: new Scaffold( - body: Form( - key: _formKey, - child: ListView( - padding: EdgeInsets.only(top: 80, left: 15, right: 15, bottom: 20), - children: [ - pinLoginLogo, - Padding( - padding: EdgeInsets.only(top: 20, bottom: 20), - child: Center( - child: LocalText(context, "welcome.pinlogin", - color: Colors.black, fontSize: 18), + + // ignore: deprecated_member_use + return WillPopScope( + onWillPop: _onWillPop, + child: LocalProgress( + inAsyncCall: _isLoading, + child: new Scaffold( + body: Form( + key: _formKey, + child: ListView( + padding: EdgeInsets.only(top: 80, left: 15, right: 15, bottom: 20), + children: [ + pinLoginLogo, + Padding( + padding: EdgeInsets.only(top: 20, bottom: 20), + child: Center( + child: LocalText(context, "welcome.pinlogin", + color: Colors.black, fontSize: 18), + ), ), - ), - LocalText( - context, - "welcome.pinlogin.fcsid", - color: Colors.black54, - fontSize: 15, - ), - fcsIdBox, - Padding( - padding: EdgeInsets.only(top: 25, bottom: 20), - child: LocalText( - context, - "welcome.pinlogin.pin", - color: Colors.black54, - fontSize: 15, - )), - pinInputBox, - loginBtn, - ], - ), - )), + LocalText( + context, + "welcome.pinlogin.fcsid", + color: Colors.black54, + fontSize: 15, + ), + fcsIdBox, + Padding( + padding: EdgeInsets.only(top: 25, bottom: 20), + child: LocalText( + context, + "welcome.pinlogin.pin", + color: Colors.black54, + fontSize: 15, + )), + pinInputBox, + loginBtn, + ], + ), + )), + ), ); } @@ -171,14 +248,14 @@ class _PinLoginPageState extends State { return; } String fcsId = "$prefixText${_fcsIdCtl.text}"; - print(fcsId); setState(() { _isLoading = true; }); try { - Navigator.pop(context, true); + await context.read().pinLogin(fcsID: fcsId, pin: pin); + Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false); } catch (e) { showMsgDialog(context, "Error", e.toString()); } finally { diff --git a/lib/pages/staff/model/staff_model.dart b/lib/pages/staff/model/staff_model.dart index 3f7a615..6603cfc 100644 --- a/lib/pages/staff/model/staff_model.dart +++ b/lib/pages/staff/model/staff_model.dart @@ -81,7 +81,7 @@ class StaffModel extends BaseModel { Future updatePin( {required String userID, required bool enablePin, - required int? pin}) async { + required String? pin}) async { await request("/employee/pin", "PUT", payload: {"id": userID, "enable_pin_login": enablePin, "pin": pin}, token: await getToken()); diff --git a/lib/pages/staff/staff_pin_editor.dart b/lib/pages/staff/staff_pin_editor.dart index 7b686e7..e13f2bd 100644 --- a/lib/pages/staff/staff_pin_editor.dart +++ b/lib/pages/staff/staff_pin_editor.dart @@ -310,7 +310,7 @@ class _StaffPinEditorState extends State { await context.read().updatePin( userID: _staff.id!, enablePin: _enablePinLogin, - pin: _enablePinLogin ? int.parse(_confirmPin) : null); + pin: _enablePinLogin ? _confirmPin : null); Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); From 966758d2f8de3b25db2ee2eedff872fd80d4f452 Mon Sep 17 00:00:00 2001 From: tzw Date: Mon, 7 Oct 2024 20:42:45 +0630 Subject: [PATCH 5/5] move reportUrl and reportProjectId from config to setting --- lib/config.dart | 25 +++------- lib/data/provider/package_data_provider.dart | 7 +-- lib/data/provider/pickup_data_provider.dart | 5 +- lib/data/provider/user_data_provider.dart | 5 +- lib/data/services/services.dart | 3 ++ lib/domain/entities/setting.dart | 46 +++++++++++-------- lib/helpers/api_helper.dart | 10 ++-- lib/main-dev.dart | 6 +-- lib/main-local.dart | 10 ++-- lib/main-prod.dart | 6 +-- .../model/consignee_selection_model.dart | 4 +- .../carton/model/sender_selection_model.dart | 5 +- .../model/fcs_shipment_model.dart | 9 ++-- lib/pages/main/model/main_model.dart | 1 + lib/pages/package/package_info.dart | 2 +- 15 files changed, 75 insertions(+), 69 deletions(-) diff --git a/lib/config.dart b/lib/config.dart index c6a4fdc..f1d0d8f 100644 --- a/lib/config.dart +++ b/lib/config.dart @@ -2,35 +2,25 @@ import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; enum Flavor { UNSET, DEV, STAGING, PRODUCTION, LOCAL } + const FlavorNames = ["Unset", "Development", "Staging", "Production", "Local"]; class Config { - static Config _instance = Config( - flavor: Flavor.UNSET, - apiURL: "", - bucketName: "", - reportProjectID: "", - reportURL: ""); + static Config _instance = Config(flavor: Flavor.UNSET, apiURL: ""); final Flavor flavor; final String name; final Color color; final String apiURL; - final String reportURL; final Level level; - final String reportProjectID; - final String bucketName; factory Config( {required Flavor flavor, required String apiURL, - required String reportURL, - required String reportProjectID, - required String bucketName, - Color color= Colors.blue, - Level level= Level.SEVERE}) { - _instance = Config._internal(flavor, FlavorNames[flavor.index], color, - apiURL, reportURL, level, reportProjectID, bucketName); + Color color = Colors.blue, + Level level = Level.SEVERE}) { + _instance = Config._internal( + flavor, FlavorNames[flavor.index], color, apiURL, level); Logger.root.level = level; Logger.root.onRecord.listen((record) { @@ -41,8 +31,7 @@ class Config { return _instance; } - Config._internal(this.flavor, this.name, this.color, this.apiURL, - this.reportURL, this.level, this.reportProjectID, this.bucketName); + Config._internal(this.flavor, this.name, this.color, this.apiURL, this.level); static Config get instance { return _instance; diff --git a/lib/data/provider/package_data_provider.dart b/lib/data/provider/package_data_provider.dart index fe05e67..a8fbc75 100644 --- a/lib/data/provider/package_data_provider.dart +++ b/lib/data/provider/package_data_provider.dart @@ -1,12 +1,13 @@ import 'dart:convert'; -import 'package:fcs/config.dart'; import 'package:fcs/constants.dart'; import 'package:fcs/domain/entities/package.dart'; import 'package:fcs/helpers/api_helper.dart'; import 'package:fcs/helpers/firebase_helper.dart'; import 'package:logging/logging.dart'; +import '../services/services.dart'; + class PackageDataProvider { final log = Logger('PackageDataProvider'); @@ -70,7 +71,7 @@ class PackageDataProvider { try { var data = await requestAPI( "/api/fts/$packages_collection/$escapePackage/$limit", "GET", - url: Config.instance.reportURL, token: await getToken()); + url: Services.setting.reportURL, token: await getToken()); if (data == null) return []; @@ -105,7 +106,7 @@ class PackageDataProvider { }; var result = await requestAPI("/api/data/packages", "POST", token: await getToken(), - url: Config.instance.reportURL, + url: Services.setting.reportURL, payload: jsonEncode(data)); if (result == null) return packages; result.forEach((d) { diff --git a/lib/data/provider/pickup_data_provider.dart b/lib/data/provider/pickup_data_provider.dart index bd5ef56..4b64572 100644 --- a/lib/data/provider/pickup_data_provider.dart +++ b/lib/data/provider/pickup_data_provider.dart @@ -4,8 +4,7 @@ import 'package:fcs/domain/entities/pickup.dart'; import 'package:fcs/helpers/api_helper.dart'; import 'package:fcs/helpers/firebase_helper.dart'; import 'package:logging/logging.dart'; - -import '../../config.dart'; +import '../services/services.dart'; class PickupDataProvider { final log = Logger('PickupDataProvider'); @@ -34,7 +33,7 @@ class PickupDataProvider { }; var result = await requestAPI("/api/data/pickups", "POST", token: await getToken(), - url: Config.instance.reportURL, + url: Services.setting.reportURL, payload: jsonEncode(data)); if (result == null) return pickups; result.forEach((d) { diff --git a/lib/data/provider/user_data_provider.dart b/lib/data/provider/user_data_provider.dart index f2077e7..f7bacdb 100644 --- a/lib/data/provider/user_data_provider.dart +++ b/lib/data/provider/user_data_provider.dart @@ -1,13 +1,14 @@ import 'dart:convert'; import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:fcs/config.dart'; import 'package:fcs/constants.dart'; import 'package:fcs/domain/entities/user.dart'; import 'package:fcs/helpers/api_helper.dart'; import 'package:fcs/helpers/firebase_helper.dart'; import 'package:logging/logging.dart'; +import '../services/services.dart'; + class UserDataProvider { final log = Logger('UserDataProvider'); @@ -66,7 +67,7 @@ class UserDataProvider { try { var data = await requestAPI( "/api/fts/$user_collection/$escapeBuyer/$limit", "GET", - url: Config.instance.reportURL, token: await getToken()); + url: Services.setting.reportURL, token: await getToken()); if (data == null) return []; diff --git a/lib/data/services/services.dart b/lib/data/services/services.dart index 9781363..6310578 100644 --- a/lib/data/services/services.dart +++ b/lib/data/services/services.dart @@ -24,6 +24,7 @@ import 'package:fcs/data/services/rate_service.dart'; import 'package:fcs/data/services/shipment_imp.dart'; import 'package:fcs/data/services/shipment_service.dart'; +import '../../domain/entities/setting.dart'; import 'auth_imp.dart'; import 'auth_service.dart'; import 'common_imp.dart'; @@ -37,6 +38,8 @@ import 'user_service.dart'; class Services { static final Services instance = Services._(); + static Setting setting = Setting(); + late AuthService _authService; late UserService _userService; diff --git a/lib/domain/entities/setting.dart b/lib/domain/entities/setting.dart index 1c553d3..3a4ddaf 100644 --- a/lib/domain/entities/setting.dart +++ b/lib/domain/entities/setting.dart @@ -10,6 +10,9 @@ List dayLists = [ class Setting { final int supportBuildNum; + final String reportURL; + final String reportProjectID; + final String bucketName; // contact page String? usaAddress; String? mmAddress; @@ -24,7 +27,7 @@ class Setting { String? about; String? courierWebsite; String? deactivateTextEn; - String? deactivateTextMm; + String? deactivateTextMm; List shipmentTypes; @@ -44,27 +47,32 @@ class Setting { this.shipmentTypes = const [], this.courierWebsite, this.deactivateTextEn, - this.deactivateTextMm}); + this.deactivateTextMm, + this.bucketName = '', + this.reportProjectID = '', + this.reportURL = ''}); factory Setting.fromMap(Map map) { return Setting( - supportBuildNum: map['support_build_number'], - inviteRequired: map['invite_required'], - appUrl: map['app_url'], - usaAddress: map['usa_address'], - mmAddress: map['mm_address'], - usaContactNumber: map['usa_contact_number'], - mmContactNumber: map['mm_contact_number'], - emailAddress: map['email_address'], - facebookLink: map['facebook_link'], - about: map['about'], - termsEng: map['terms_eng_markdown'], - termsMm: map['terms_mm_markdown'], - shipmentTypes: List.from(map['shipment_types']), - courierWebsite: map['courier_website'], - deactivateTextEn: map['deactivate_text_en']??"", - deactivateTextMm: map['deactivate_text_mm']??"" - ); + supportBuildNum: map['support_build_number'], + inviteRequired: map['invite_required'], + appUrl: map['app_url'], + usaAddress: map['usa_address'], + mmAddress: map['mm_address'], + usaContactNumber: map['usa_contact_number'], + mmContactNumber: map['mm_contact_number'], + emailAddress: map['email_address'], + facebookLink: map['facebook_link'], + about: map['about'], + termsEng: map['terms_eng_markdown'], + termsMm: map['terms_mm_markdown'], + shipmentTypes: List.from(map['shipment_types']), + courierWebsite: map['courier_website'], + deactivateTextEn: map['deactivate_text_en'] ?? "", + deactivateTextMm: map['deactivate_text_mm'] ?? "", + reportProjectID: map['report_project_id'] ?? "", + reportURL: map['report_url'] ?? "", + bucketName: map['bucket_name'] ?? ''); } @override diff --git a/lib/helpers/api_helper.dart b/lib/helpers/api_helper.dart index 81fa7e5..64330fe 100644 --- a/lib/helpers/api_helper.dart +++ b/lib/helpers/api_helper.dart @@ -37,7 +37,7 @@ Future requestAPI(String path, method, if (devInfo.deviceID != null) { headers["Device"] = devInfo.deviceID ?? "" + ":" + deviceName; } - headers["Project-ID"] = Config.instance.reportProjectID; + headers["Project-ID"] = Services.setting.reportProjectID; BaseOptions options = new BaseOptions( method: method, @@ -99,7 +99,7 @@ Future requestDownloadAPI(String path, method, var _downloadData = StringBuffer(); var fileSave = new File(filePath!); var request = await client.getUrl(Uri.parse("$baseUrl$path")); - request.headers.set("Project-ID", Config.instance.reportProjectID); + request.headers.set("Project-ID", Services.setting.reportProjectID); request.headers .set(HttpHeaders.contentTypeHeader, "application/json; charset=UTF-8"); if (token != null) { @@ -140,7 +140,7 @@ Future requestDownloadPDFAPI(String path, method, // var _downloadData = StringBuffer(); var fileSave = new File(filePath!); var request = await client.getUrl(Uri.parse("$baseUrl$path")); - request.headers.set("Project-ID", Config.instance.reportProjectID); + request.headers.set("Project-ID", Services.setting.reportProjectID); if (token != null) { request.headers.set("Token", token); } @@ -188,7 +188,7 @@ Future requestDownload(String path, method, HttpClient client = new HttpClient(); // var _downloadData = StringBuffer(); var request = await client.postUrl(Uri.parse("$baseUrl$path")); - request.headers.set("Project-ID", Config.instance.reportProjectID); + request.headers.set("Project-ID", Services.setting.reportProjectID); if (token != null) { request.headers.set("Token", token); } @@ -244,7 +244,7 @@ Future downloadPDF( await requestDownload("/api/pdf", "POST", filePath: path, - url: Config.instance.reportURL, + url: Services.setting.reportURL, token: await getToken(), payload: jsonEncode(data), onDownloadDone: (f) async { completer.complete(f); diff --git a/lib/main-dev.dart b/lib/main-dev.dart index def8841..382aeb2 100644 --- a/lib/main-dev.dart +++ b/lib/main-dev.dart @@ -21,9 +21,9 @@ Future main() async { color: Colors.blue, apiURL: "http://192.168.100.150:9090", // apiURL: "https://asia-northeast1-fcs-dev1.cloudfunctions.net/API13", - reportURL: "http://petrok.mokkon.com:7071", - reportProjectID: "fcs-dev", - bucketName: "gs://fcs-dev1-us", + // reportURL: "http://petrok.mokkon.com:7071", + // reportProjectID: "fcs-dev", + // bucketName: "gs://fcs-dev1-us", level: Level.ALL); runApp(App(title: "FCS - Dev")); } diff --git a/lib/main-local.dart b/lib/main-local.dart index b52b95f..8d85745 100644 --- a/lib/main-local.dart +++ b/lib/main-local.dart @@ -9,10 +9,12 @@ void main() { Config( flavor: Flavor.DEV, color: Colors.blue, - reportURL: "http://petrok.mokkon.com:8091", - reportProjectID: "fcs-dev", apiURL: "http://192.168.1.155:7777", - bucketName: "gs://fcs-dev1-us", + // reportURL: "http://petrok.mokkon.com:8091", + // reportProjectID: "fcs-dev", + // bucketName: "gs://fcs-dev1-us", level: Level.ALL); - runApp(App(title: "FCS - Dev local",)); + runApp(App( + title: "FCS - Dev local", + )); } diff --git a/lib/main-prod.dart b/lib/main-prod.dart index 9b67e7c..800690c 100644 --- a/lib/main-prod.dart +++ b/lib/main-prod.dart @@ -16,9 +16,9 @@ Future main() async { flavor: Flavor.PRODUCTION, color: Colors.blue, apiURL: "https://asia-northeast1-fcs-prod1.cloudfunctions.net/API13", - reportURL: "http://petrok.mokkon.com:8092", - reportProjectID: "fcs-prod", - bucketName: "gs://fcs-prod1", + // reportURL: "http://petrok.mokkon.com:8092", + // reportProjectID: "fcs-prod", + // bucketName: "gs://fcs-prod1", level: Level.ALL); runApp(App(title: "FCS")); } diff --git a/lib/pages/carton/model/consignee_selection_model.dart b/lib/pages/carton/model/consignee_selection_model.dart index 7377de4..44e1545 100644 --- a/lib/pages/carton/model/consignee_selection_model.dart +++ b/lib/pages/carton/model/consignee_selection_model.dart @@ -2,10 +2,10 @@ import 'dart:async'; import 'dart:convert'; import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:fcs/config.dart'; import 'package:logging/logging.dart'; import '../../../constants.dart'; +import '../../../data/services/services.dart'; import '../../../domain/entities/user.dart'; import '../../../helpers/api_helper.dart'; import '../../../helpers/firebase_helper.dart'; @@ -56,7 +56,7 @@ class ConsigneeSelectionModel extends BaseModel { var result = await requestAPI( "/api/fts/$user_collection/$escapeSender/$rowPerPage", "GET", - url: Config.instance.reportURL, token: await getToken()); + url: Services.setting.reportURL, token: await getToken()); if (result != null) { for (var row in result) { diff --git a/lib/pages/carton/model/sender_selection_model.dart b/lib/pages/carton/model/sender_selection_model.dart index 8179099..f69fa30 100644 --- a/lib/pages/carton/model/sender_selection_model.dart +++ b/lib/pages/carton/model/sender_selection_model.dart @@ -3,9 +3,8 @@ import 'dart:convert'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:logging/logging.dart'; - -import '../../../config.dart'; import '../../../constants.dart'; +import '../../../data/services/services.dart'; import '../../../domain/entities/user.dart'; import '../../../helpers/api_helper.dart'; import '../../../helpers/firebase_helper.dart'; @@ -57,7 +56,7 @@ class SenderSelectionModel extends BaseModel { var result = await requestAPI( "/api/fts/$user_collection/$escapeSender/$rowPerPage", "GET", - url: Config.instance.reportURL, token: await getToken()); + url: Services.setting.reportURL, token: await getToken()); if (result != null) { for (var row in result) { diff --git a/lib/pages/fcs_shipment/model/fcs_shipment_model.dart b/lib/pages/fcs_shipment/model/fcs_shipment_model.dart index fe2863c..de1c285 100644 --- a/lib/pages/fcs_shipment/model/fcs_shipment_model.dart +++ b/lib/pages/fcs_shipment/model/fcs_shipment_model.dart @@ -157,9 +157,12 @@ class FcsShipmentModel extends BaseModel { void initUser(user) { super.initUser(user); - _loadShipmentTypes(); - _loadShipmentConsignees(); - _loadShipmentPorts(); + + if (user != null && user.hasFcsShipments()) { + _loadShipmentTypes(); + _loadShipmentConsignees(); + _loadShipmentPorts(); + } } @override diff --git a/lib/pages/main/model/main_model.dart b/lib/pages/main/model/main_model.dart index 781241f..6952650 100644 --- a/lib/pages/main/model/main_model.dart +++ b/lib/pages/main/model/main_model.dart @@ -141,6 +141,7 @@ class MainModel extends ChangeNotifier { try { Services.instance.authService.getSetting().listen((event) { this.setting = event; + Services.setting = event; models.forEach((m) => m.initSetting(setting)); notifyListeners(); }); diff --git a/lib/pages/package/package_info.dart b/lib/pages/package/package_info.dart index c6285c9..421526c 100644 --- a/lib/pages/package/package_info.dart +++ b/lib/pages/package/package_info.dart @@ -133,7 +133,7 @@ class _PackageInfoState extends State { if (d == null) return; _changeDeliverayAddress(d); } - : () {}, + : null, ); return LocalProgress(