diff --git a/lib/domain/entities/cargo_type.dart b/lib/domain/entities/cargo_type.dart index 22cd0dd..29a49ab 100644 --- a/lib/domain/entities/cargo_type.dart +++ b/lib/domain/entities/cargo_type.dart @@ -1,3 +1,5 @@ +import 'package:fcs/pages/main/util.dart'; + class CargoType { String? id; String? name; @@ -96,7 +98,7 @@ class CargoType { Map toMapForCarton() { return { "id": id, - 'weight': weight, + 'weight': double.tryParse(twoDecimalFormatted(weight)) ?? 0.00, "mix_cargo_type_ids": mixCargoes.map((e) => e.id).toList() }; } @@ -109,6 +111,22 @@ class CargoType { return CargoType.fromMap(toMap(), id!); } + CargoType cloneForCarton() { + return CargoType( + id: id, + name: name, + weight: weight, + isDefault: isDefault, + displayIndex: displayIndex, + isMixCargo: isMixCargo, + mixCargoIds: List.from(mixCargoIds), + mixCargoes: List.from(mixCargoes)); + } + + CargoType cloneForSurchage() { + return CargoType(id: id, name: name, qty: qty); + } + @override bool operator ==(Object other) => other is CargoType && other.id == id; diff --git a/lib/domain/entities/carton.dart b/lib/domain/entities/carton.dart index 5182116..74235d7 100644 --- a/lib/domain/entities/carton.dart +++ b/lib/domain/entities/carton.dart @@ -167,8 +167,8 @@ class Carton { Map toMap() { var _types = cargoTypes.where((t) => t.weight != 0).toList(); var _cargoTypes = _types.map((c) => c.toMapForCarton()).toList(); - - var _packagesIds = packages.map((c) => c.id).toList(); + + // var _packagesIds = packages.map((c) => c.id).toList(); var _surchareItems = surchareItems.map((c) => c.toMapForSurcharge()).toList(); @@ -184,7 +184,7 @@ class Carton { 'length': length, 'width': width, 'height': height, - 'package_ids': _packagesIds, + // 'package_ids': _packagesIds, 'cargo_types': _cargoTypes, 'surcharge_items': _surchareItems, }; diff --git a/lib/pages/carton/cargo_widget.dart b/lib/pages/carton/cargo_widget.dart index bedc1d5..33016d5 100644 --- a/lib/pages/carton/cargo_widget.dart +++ b/lib/pages/carton/cargo_widget.dart @@ -47,14 +47,18 @@ class _CargoWidgetState extends State { List _cargoTypes = []; List _surchareItems = []; TextEditingController totalCtl = TextEditingController(); + List cargoTypeControllers = []; List surchargeControllers = []; - List _mixCargoTypes = []; - List mixCargoTypeControllers = []; + bool get hasValueTotalWeight => + totalCtl.text.isNotEmpty && totalCtl.text != '0.00'; + bool get hasValueCargoes => + _cargoTypes.isNotEmpty && _cargoTypes.every((e) => e.weight != 0.00); - bool get isKnownTotalWeight => - totalCtl.text.isNotEmpty && totalCtl.text != '0'; + double get actualTotalWeight => + _cargoTypes.fold(0, (sum, value) => sum + value.weight); + String? error; @override void initState() { @@ -63,25 +67,17 @@ class _CargoWidgetState extends State { } _init() { - totalCtl.addListener(totalInputChangeListener); // for cargo types if (widget.cargoTypes.isNotEmpty) { - List allCargoes = List.from(widget.cargoTypes); - _cargoTypes = allCargoes.where((e) => !e.isMixCargo).toList(); + _cargoTypes = List.from(widget.cargoTypes); for (var e in _cargoTypes) { var editor = TextEditingController(); - editor.text = removeTrailingZeros(e.weight); + editor.text = twoDecimalFormatted( + double.tryParse(removeTrailingZeros(e.weight)) ?? 0); editor.addListener(inputChangeListener); cargoTypeControllers.add(editor); } - _mixCargoTypes = allCargoes.where((e) => e.isMixCargo).toList(); - for (var e in _mixCargoTypes) { - var editor = TextEditingController(); - editor.text = removeTrailingZeros(e.weight); - editor.addListener(inputChangeListener); - mixCargoTypeControllers.add(editor); - } - _calculateTotalWeght(); + _onPopulate(); } else { WidgetsBinding.instance.addPostFrameCallback((_) { _openCargoTypeSelection(); @@ -94,7 +90,7 @@ class _CargoWidgetState extends State { for (var e in _surchareItems) { var editor = TextEditingController(); editor.text = e.qty.toString(); - editor.addListener(inputChangeListenerForSurchage); + editor.addListener(inputChangeListener); surchargeControllers.add(editor); } } @@ -104,85 +100,89 @@ class _CargoWidgetState extends State { } } - totalInputChangeListener() { - print("Listen isKnownTotalWeight:$isKnownTotalWeight"); - setState(() {}); - } - - inputChangeListenerForSurchage() { - setState(() {}); - } - _openCargoTypeSelection() async { List? cargoes = await showDialog( context: context, builder: (_) => const CargoTypeAdditionDialog()); if (cargoes == null) return; - _cargoTypes = cargoes.where((e) => !e.isMixCargo).toList(); - _mixCargoTypes = cargoes.where((e) => e.isMixCargo).toList(); + _cargoTypes = cargoes; + _cargoTypes.sort((a, b) => (a == b ? 0 : (a.isMixCargo ? 1 : -1))); for (var e in _cargoTypes) { var editor = TextEditingController(); - editor.text = '0'; + editor.text = '0.00'; editor.addListener(inputChangeListener); cargoTypeControllers.add(editor); } - for (var e in _mixCargoTypes) { - var editor = TextEditingController(); - editor.text = '0'; - editor.addListener(inputChangeListener); - mixCargoTypeControllers.add(editor); - } - _calculateTotalWeght(); + totalCtl.text = twoDecimalFormatted( + double.tryParse(removeTrailingZeros(actualTotalWeight)) ?? 0); + if (mounted) { setState(() {}); } } - _calculateTotalWeght() { - print("_calculateTotalWeght isKnownTotalWeight:$isKnownTotalWeight"); - double notMixTotal = - _cargoTypes.fold(0, (sum, value) => sum + value.weight); - double mixTotal = - _mixCargoTypes.fold(0, (sum, value) => sum + value.weight); - double total = notMixTotal + mixTotal; - if (isKnownTotalWeight) { + inputChangeListener() { + setState(() {}); + } + + List getEmptyFields() { + List emptyFields = []; + for (int i = 0; i < cargoTypeControllers.length; i++) { + if (cargoTypeControllers[i].text.trim().isEmpty || + cargoTypeControllers[i].text.trim() == "0") { + emptyFields.add(i); + } + } + return emptyFields; + } + + void _onPopulate() { + if (!hasValueTotalWeight && hasValueCargoes) { + totalCtl.text = twoDecimalFormatted( + double.tryParse(removeTrailingZeros(actualTotalWeight)) ?? 0); + error = null; } else { - totalCtl.text = removeTrailingZeros(total); + // auto populate remaining value + double totalWeight = (double.tryParse(totalCtl.text) ?? 0); + + if (actualTotalWeight > totalWeight) { + error = "Exceed total weight"; + } else { + error = null; + double remainingWeight = + (totalWeight - actualTotalWeight).clamp(0, totalWeight); + + List emptyFieldIndexes = getEmptyFields(); + + if (emptyFieldIndexes.isNotEmpty) { + if (emptyFieldIndexes.length == 1) { + _cargoTypes.asMap().entries.forEach((e) { + if (e.value.weight == 0) { + e.value.weight = remainingWeight; + cargoTypeControllers[e.key].text = twoDecimalFormatted( + double.tryParse(removeTrailingZeros(e.value.weight)) ?? 0); + } + }); + } + } + } + } + if (mounted) { + setState(() {}); } } - // bool isFieldEmpty(int index) { - // return cargoTypeControllers[index].text.isEmpty; - // } - - // List getEmptyFields() { - // List emptyFields = []; - // for (int i = 0; i < cargoTypeControllers.length; i++) { - // if (isFieldEmpty(i)) { - // emptyFields.add(i); - // } - // } - // return emptyFields; - // } - - inputChangeListener() { - _cargoTypes.asMap().entries.forEach((e) { - _cargoTypes[e.key].weight = - double.tryParse(cargoTypeControllers[e.key].text) ?? 0; - }); - _mixCargoTypes.asMap().entries.forEach((e) { - _mixCargoTypes[e.key].weight = - double.tryParse(mixCargoTypeControllers[e.key].text) ?? 0; - }); - double notMixTotal = - _cargoTypes.fold(0, (sum, value) => sum + value.weight); - double mixTotal = - _mixCargoTypes.fold(0, (sum, value) => sum + value.weight); - double total = notMixTotal + mixTotal; - setState(() { - totalCtl.text = removeTrailingZeros(total); - }); + void _onCheckTotalWeight(String value) { + double totalWeight = double.tryParse(value) ?? 0; + if (totalWeight != actualTotalWeight) { + error = "Invalid total weight"; + } else { + error = null; + } + if (mounted) { + setState(() {}); + } } @override @@ -218,240 +218,93 @@ class _CargoWidgetState extends State { color: primaryColor, ), onPressed: () async { - List allCargoTypes = _cargoTypes + _mixCargoTypes; CargoType? cargoType = await Navigator.push( context, CupertinoPageRoute( builder: (context) => - CargoTypeAddition(cargoTypes: allCargoTypes))); + CargoTypeAddition(cargoTypes: _cargoTypes))); if (cargoType == null) return; - if (cargoType.isMixCargo) { - _mixCargoTypes.add(cargoType); - mixCargoTypeControllers.clear(); - for (var e in _mixCargoTypes) { - var editor = TextEditingController(); - editor.text = removeTrailingZeros(e.weight); - editor.addListener(inputChangeListener); - mixCargoTypeControllers.add(editor); + // add cargo type + if (cargoType.isMixCargo) { + int lastTrueIndex = + _cargoTypes.lastIndexWhere((e) => e.isMixCargo); + if (lastTrueIndex != -1) { + _cargoTypes.insert(lastTrueIndex + 1, cargoType); + } else { + _cargoTypes.add(cargoType); } } else { - _cargoTypes.add(cargoType); - cargoTypeControllers.clear(); - - for (var e in _cargoTypes) { - var editor = TextEditingController(); - editor.text = removeTrailingZeros(e.weight); - editor.addListener(inputChangeListener); - cargoTypeControllers.add(editor); + int lastFalseIndex = + _cargoTypes.lastIndexWhere((e) => !e.isMixCargo); + if (lastFalseIndex != -1) { + _cargoTypes.insert(lastFalseIndex + 1, cargoType); + } else { + _cargoTypes.insert(0, cargoType); } } - _calculateTotalWeght(); + + cargoTypeControllers.clear(); + for (var e in _cargoTypes) { + var editor = TextEditingController(); + editor.text = twoDecimalFormatted( + double.tryParse(removeTrailingZeros(e.weight)) ?? 0); + editor.addListener(inputChangeListener); + cargoTypeControllers.add(editor); + } if (mounted) { setState(() {}); } }), ); - final cargosBox = Padding( - padding: const EdgeInsets.only(top: 10), - child: Wrap( - alignment: WrapAlignment.spaceBetween, - runSpacing: 15, - children: _cargoTypes.asMap().entries.map((e) { - var key = e.key; - var c = e.value; - return SizedBox( + final totalWeightBox = Padding( + padding: const EdgeInsets.only(top: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( width: MediaQuery.of(context).size.width / 2.3, child: Row( children: [ InkResponse( - radius: 23, + radius: 25, onTap: () { - _cargoTypes.removeAt(key); - - double totalWeight = - double.tryParse(totalCtl.text) ?? 0; - double removeWeight = - (double.tryParse(cargoTypeControllers[key].text) ?? - 0); - - if (totalWeight >= removeWeight) { - double result = totalWeight - removeWeight; - totalCtl.text = removeTrailingZeros(result); - } - - cargoTypeControllers[key].clear(); - - if (mounted) { - setState(() {}); - } + setState(() { + totalCtl.clear(); + }); + _onCheckTotalWeight(totalCtl.text); }, - child: Icon(Feather.minus_circle, color: labelColor)), + child: Icon(MaterialIcons.clear, color: labelColor)), const SizedBox(width: 10), Flexible( child: inputTextFieldWidget(context, - lableText: c.name ?? "", - controller: cargoTypeControllers[key], + lableText: "Total", + controller: totalCtl, onFieldSubmitted: (newValue) { + if (hasValueCargoes) { + _onCheckTotalWeight(newValue); + } else { + _onPopulate(); + } + }, suffixIcon: InkResponse( radius: 23, - onTap: () {}, + onTap: () { + setState(() { + totalCtl.text = twoDecimalFormatted( + double.tryParse(removeTrailingZeros( + actualTotalWeight)) ?? + 0); + error = null; + }); + }, child: Icon(Ionicons.md_refresh_circle, - color: labelColor))), + color: labelColor, size: 22))), ), ], - ), - ); - }).toList()), - ); - - final mixCargosBox = Wrap( - alignment: WrapAlignment.spaceBetween, - runSpacing: 15, - children: _mixCargoTypes.asMap().entries.map((e) { - var key = e.key; - var c = e.value; - return SizedBox( - width: MediaQuery.of(context).size.width / 2.3, - child: Column( - children: [ - Row( - children: [ - InkResponse( - radius: 23, - onTap: () { - _mixCargoTypes.removeAt(key); - - double totalWeight = - double.tryParse(totalCtl.text) ?? 0; - double removeWeight = (double.tryParse( - mixCargoTypeControllers[key].text) ?? - 0); - - if (totalWeight >= removeWeight) { - double result = totalWeight - removeWeight; - totalCtl.text = removeTrailingZeros(result); - } - - mixCargoTypeControllers[key].clear(); - - if (mounted) { - setState(() {}); - } - }, - child: Icon(Feather.minus_circle, - color: labelColor, size: 20)), - const SizedBox(width: 10), - Flexible( - child: inputTextFieldWidget(context, - lableText: c.name ?? "", - controller: mixCargoTypeControllers[key], - suffixIcon: InkResponse( - radius: 23, - onTap: () {}, - child: Icon(Ionicons.md_refresh_circle, - color: labelColor, size: 22))), - ), - InkResponse( - radius: 23, - onTap: () async { - List? cargoes = await showDialog( - context: context, - builder: (_) => MixCargoTypeAdditionDialog( - cargoTypes: c.mixCargoes)); - if (cargoes == null) return; - setState(() { - c.mixCargoes = List.from(cargoes); - }); - }, - child: - Icon(Icons.add_circle, color: labelColor, size: 22)) - ], - ), - c.mixCargoes.isEmpty - ? const SizedBox() - : Padding( - padding: const EdgeInsets.only(top: 5), - child: Column( - children: c.mixCargoes.map((e) { - return Padding( - padding: const EdgeInsets.only(top: 12), - child: Row( - children: [ - const SizedBox(width: 25), - InkResponse( - radius: 23, - onTap: () { - setState(() { - c.mixCargoes.remove(e); - }); - }, - child: Icon(Feather.minus_circle, - color: labelColor, size: 20)), - Padding( - padding: const EdgeInsets.only(left: 10), - child: Text(e.name ?? ""), - ), - ], - ), - ); - }).toList()), - ) - ], - ), - ); - }).toList()); - - final totalWeightBox = Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - SizedBox( - width: MediaQuery.of(context).size.width / 2.3, - child: Row( - children: [ - InkResponse( - radius: 25, - onTap: () { - setState(() { - totalCtl.clear(); - }); - }, - child: Icon(MaterialIcons.clear, color: labelColor)), - const SizedBox(width: 10), - Flexible( - child: inputTextFieldWidget(context, - lableText: "Total", - controller: totalCtl, - // onChanged: (neValue) { - // List emptyFields = getEmptyFields(); - // if (emptyFields.length == 1) { - // double totalWeight = double.tryParse(neValue) ?? 0; - - // _cargoTypes.asMap().entries.forEach((e) { - // _cargoTypes[e.key].weight = - // double.tryParse(cargoTypeControllers[e.key].text) ?? - // 0; - // }); - // double result = _cargoTypes.fold( - // 0, (sum, value) => sum + value.weight); - - // if (totalWeight >= result) { - // double remaining = totalWeight - result; - // setState(() { - // cargoTypeControllers[emptyFields.first].text = - // removeTrailingZeros(remaining); - // }); - // } - // } - // }, - suffixIcon: InkResponse( - radius: 23, - child: Icon(Ionicons.md_refresh_circle, - color: labelColor))), - ), - ], - )), - ], + )), + ], + ), ); final subchargeItemTitleBox = LocalTitle( @@ -485,10 +338,10 @@ class _CargoWidgetState extends State { ); final subChargeItemsBox = Padding( - padding: const EdgeInsets.only(top: 10), + padding: const EdgeInsets.only(top: 5), child: Wrap( alignment: WrapAlignment.spaceBetween, - runSpacing: 15, + runSpacing: 25, children: _surchareItems.asMap().entries.map((e) { var key = e.key; var c = e.value; @@ -533,20 +386,160 @@ class _CargoWidgetState extends State { return; } - var allCargoes = _cargoTypes + _mixCargoTypes; - - widget.onContinue!(allCargoes, _surchareItems); + if (error != null) { + showMsgDialog( + context, "Error", "Please add the right cargo type weight"); + return; + } + widget.onContinue!(_cargoTypes, _surchareItems); } }, ); final previousBtn = PreviousButton(onTap: () { + if (error != null) { + showMsgDialog( + context, "Error", "Please add the right cargo type weight"); + return; + } if (widget.onPrevious != null) { - var allCargoes = _cargoTypes + _mixCargoTypes; - widget.onPrevious!(allCargoes, _surchareItems); + widget.onPrevious!(_cargoTypes, _surchareItems); } }); + Widget cargoesWidget(List items) { + List widgets = []; + for (int i = 0; i < items.length; i++) { + var key = i; + var c = items[i]; + + if (i > 0 && (!items[i - 1].isMixCargo && items[i].isMixCargo)) { + widgets.add(Padding( + padding: const EdgeInsets.symmetric(horizontal: 70), + child: Divider(color: Colors.grey.shade300))); + } + + widgets.add(SizedBox( + width: MediaQuery.of(context).size.width / 2.3, + child: Column( + children: [ + Row( + children: [ + InkResponse( + radius: 25, + onTap: () { + _cargoTypes.removeAt(key); + cargoTypeControllers.removeAt(key); + + _onCheckTotalWeight(totalCtl.text); + + if (mounted) { + setState(() {}); + } + }, + child: Icon(Feather.minus_circle, color: labelColor)), + const SizedBox(width: 10), + Flexible( + child: inputTextFieldWidget(context, + lableText: c.name ?? "", + controller: cargoTypeControllers[key], + onFieldSubmitted: (value) { + _cargoTypes[key].weight = double.tryParse(value) ?? 0; + _onPopulate(); + }, + suffixIcon: InkResponse( + radius: 23, + onTap: () { + double totalWeight = + (double.tryParse(totalCtl.text) ?? 0); + + var list = _cargoTypes + .where((e) => e.id != c.id) + .toList(); + double sum = (list.fold( + 0, (sum, value) => sum + value.weight)); + + if (sum > totalWeight) { + error = "Exceed total weight"; + } else { + error = null; + double resetValue = totalWeight - sum; + + setState(() { + c.weight = resetValue; + cargoTypeControllers[key].text = + twoDecimalFormatted(double.tryParse( + removeTrailingZeros( + resetValue)) ?? + 0); + }); + _onPopulate(); + } + }, + child: Icon(Ionicons.md_refresh_circle, + color: labelColor, size: 22))), + ), + c.isMixCargo + ? InkResponse( + radius: 23, + onTap: () async { + List? cargoes = await showDialog( + context: context, + builder: (_) => MixCargoTypeAdditionDialog( + cargoTypes: c.mixCargoes)); + if (cargoes == null) return; + setState(() { + c.mixCargoes = List.from(cargoes); + }); + }, + child: Icon(Icons.add_circle, + color: labelColor, size: 22)) + : const SizedBox() + ], + ), + c.mixCargoes.isEmpty + ? const SizedBox() + : Padding( + padding: const EdgeInsets.only(top: 5), + child: Column( + children: c.mixCargoes.map((e) { + return Padding( + padding: const EdgeInsets.only(top: 12), + child: Row( + children: [ + const SizedBox(width: 25), + InkResponse( + radius: 23, + onTap: () { + setState(() { + c.mixCargoes.remove(e); + }); + }, + child: Icon(Feather.minus_circle, + color: labelColor, size: 20)), + Padding( + padding: const EdgeInsets.only(left: 10), + child: Text(e.name ?? ""), + ), + ], + ), + ); + }).toList()), + ) + ], + ), + )); + } + + return Padding( + padding: const EdgeInsets.only(top: 5), + child: Wrap( + alignment: WrapAlignment.spaceBetween, + runSpacing: 25, + children: widgets), + ); + } + return Column( children: [ Expanded( @@ -557,19 +550,20 @@ class _CargoWidgetState extends State { const SizedBox(height: 8), userRow, cargoTitle, - cargosBox, - _mixCargoTypes.isNotEmpty && _cargoTypes.isNotEmpty - ? Padding( - padding: const EdgeInsets.symmetric( - horizontal: 100, vertical: 25), - child: Divider(color: Colors.grey.shade300), + cargoesWidget(_cargoTypes), + const SizedBox(height: 15), + _cargoTypes.isNotEmpty + ? Divider( + color: Colors.grey.shade300, + ) + : const SizedBox(), + const SizedBox(height: 5), + error != null + ? Text( + error!, + style: TextStyle(color: dangerColor), ) : const SizedBox(), - mixCargosBox, - Padding( - padding: const EdgeInsets.symmetric(vertical: 25), - child: Divider(color: Colors.grey.shade300), - ), totalWeightBox, subchargeItemTitleBox, subChargeItemsBox, @@ -599,6 +593,7 @@ class _CargoWidgetState extends State { {required String lableText, TextEditingController? controller, Function(String)? onChanged, + Function(String)? onFieldSubmitted, bool readOnly = false, Widget? suffixIcon}) { return TextFormField( @@ -607,9 +602,14 @@ class _CargoWidgetState extends State { cursorColor: primaryColor, keyboardType: TextInputType.number, onChanged: onChanged, + onFieldSubmitted: onFieldSubmitted, readOnly: readOnly, decoration: InputDecoration( - suffixIcon: suffixIcon, + suffixIcon: Padding( + padding: const EdgeInsets.only(right: 8), + child: suffixIcon, + ), + suffixIconConstraints: BoxConstraints(minWidth: 0, minHeight: 0), contentPadding: EdgeInsets.all(0), labelText: lableText, labelStyle: newLabelStyle(color: Colors.black54, fontSize: 17), diff --git a/lib/pages/carton/carton_info.dart b/lib/pages/carton/carton_info.dart index 5754636..4ddbc29 100644 --- a/lib/pages/carton/carton_info.dart +++ b/lib/pages/carton/carton_info.dart @@ -7,7 +7,6 @@ import 'package:fcs/domain/entities/package.dart'; import 'package:fcs/helpers/theme.dart'; import 'package:fcs/pages/carton/carton_image_upload_editor.dart'; import 'package:fcs/pages/main/util.dart'; -import 'package:fcs/pages/package/model/package_model.dart'; import 'package:fcs/pages/widgets/display_text.dart'; import 'package:fcs/pages/widgets/local_app_bar.dart'; import 'package:fcs/pages/widgets/local_text.dart'; @@ -28,6 +27,7 @@ import 'carton_package_editor.dart'; import 'mix_carton/mix_carton_editor.dart'; import 'mix_carton_detail_list.dart'; import 'model/carton_model.dart'; +import 'model/package_selection_model.dart'; import 'package_detail_list.dart'; import 'print_qr_code_page.dart'; @@ -48,10 +48,11 @@ class _CartonInfoState extends State { List _cargoTypes = []; List _surchareItems = []; List _mixCartons = []; - List _packages = []; - double totalWeight = 0.0; - double totalSurchargeCount = 0.0; - CartonSize? standardSize; + + double _totalWeight = 0.0; + int _packageCount = 0; + + CartonSize? _standardSize; FcsShipment? _shipment; @override @@ -77,7 +78,7 @@ class _CartonInfoState extends State { bool isStandartSize = sameLength && sameWidth && sameHeight; if (isStandartSize) { _carton.cartonSizeType = standardCarton; - standardSize = cartonSizes.firstWhere((size) => + _standardSize = cartonSizes.firstWhere((size) => size.length == _carton.length && size.width == _carton.width && size.height == _carton.height); @@ -89,32 +90,11 @@ class _CartonInfoState extends State { _carton.cartonSizeType = customCarton; } - if (widget.carton.fcsShipmentID != null && - widget.carton.fcsShipmentID != '') { - var s = await context - .read() - .getFcsShipment(widget.carton.fcsShipmentID!); - _shipment = s; - } + await Future.wait( + [_loadFcsShipment(), _loadPackageCount(), _loadMixCargoes()]); - if (_carton.cartonType == carton_from_packages) { - _packages = await context - .read() - .getPackagesByIds(_carton.packageIDs); - } - - if (_carton.cartonType == mix_carton) { - _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 = + _totalWeight = _carton.cargoTypes.fold(0, (sum, value) => sum + value.weight); - totalSurchargeCount = - _surchareItems.fold(0, (sum, value) => sum + value.qty); } finally { _isLoading = false; } @@ -124,12 +104,50 @@ class _CartonInfoState extends State { } } + Future _loadFcsShipment() async { + if (widget.carton.fcsShipmentID != null && + widget.carton.fcsShipmentID != '') { + var s = await context + .read() + .getFcsShipment(widget.carton.fcsShipmentID!); + _shipment = s; + if (mounted) { + setState(() {}); + } + } + } + + Future _loadPackageCount() async { + if (_carton.cartonType == carton_from_packages) { + int? count = await context.read().getPackageCount( + senderId: widget.carton.senderID ?? "", + consigneeId: widget.carton.consigneeID ?? "", + shipmentId: widget.carton.fcsShipmentID ?? ""); + _packageCount = count ?? 0; + if (mounted) { + setState(() {}); + } + } + } + + Future _loadMixCargoes() async { + if (_carton.cartonType == mix_carton) { + _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!)); + if (mounted) { + setState(() {}); + } + } + } + @override Widget build(BuildContext context) { 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()}”" + ? "${_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; @@ -178,15 +196,19 @@ class _CartonInfoState extends State { final packageLengthBox = DisplayText( showLabelLink: true, - subText: Text(numberFormatter.format(_packages.length)), + subText: Text(numberFormatter.format(_packageCount)), labelTextKey: "box.package", onTapLabel: () { Navigator.push( context, CupertinoPageRoute( builder: (context) => PackageDetailList( - cartonNumber: _carton.cartonNumber ?? '', - packages: _packages))); + cartonNumber: _carton.cartonNumber ?? '', + packageCount: _packageCount, + senderID: _carton.senderID ?? "", + consingeeID: _carton.consigneeID ?? "", + fcsShipmentID: _carton.fcsShipmentID ?? "", + ))); }, ); @@ -278,7 +300,8 @@ class _CartonInfoState extends State { _cargoTypes.isNotEmpty ? Padding( padding: EdgeInsets.only(right: 0), - child: Text("${removeTrailingZeros(totalWeight)} lb", + child: Text( + "${twoDecimalFormatted(double.tryParse(removeTrailingZeros(_totalWeight)) ?? 0)} lb", textAlign: TextAlign.end, style: TextStyle(color: Colors.black54, fontSize: 15))) : const SizedBox() @@ -294,7 +317,7 @@ class _CartonInfoState extends State { return Padding( padding: const EdgeInsets.symmetric(vertical: 2), child: Container( - color: e.key.isEven ? Colors.grey.shade300 : oddColor, + color: e.key.isEven ? Colors.grey.shade200 : oddColor, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -307,7 +330,7 @@ class _CartonInfoState extends State { color: Colors.black, fontSize: 15), ), Text( - "${removeTrailingZeros(e.value.weight)} lb", + "${twoDecimalFormatted(double.tryParse(removeTrailingZeros(e.value.weight)) ?? 0)} lb", textAlign: TextAlign.end, style: TextStyle( color: Colors.black, fontSize: 15)) @@ -325,7 +348,9 @@ class _CartonInfoState extends State { vertical: 2), child: Text( "- ${c.name}", - style: TextStyle(fontSize: 14), + style: TextStyle( + fontSize: 14, + color: labelColor), ), ); }).toList()), @@ -357,7 +382,7 @@ class _CartonInfoState extends State { return Padding( padding: const EdgeInsets.symmetric(vertical: 2), child: Container( - color: e.key.isEven ? Colors.grey.shade300 : oddColor, + color: e.key.isEven ? Colors.grey.shade200 : oddColor, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -526,7 +551,7 @@ class _CartonInfoState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible(child: cartonSizeBox), - _packages.isEmpty + _packageCount == 0 ? const SizedBox() : Flexible(child: packageLengthBox), ], diff --git a/lib/pages/carton/carton_package_editor.dart b/lib/pages/carton/carton_package_editor.dart index c082be7..7e561e7 100644 --- a/lib/pages/carton/carton_package_editor.dart +++ b/lib/pages/carton/carton_package_editor.dart @@ -13,12 +13,10 @@ import '../../../domain/vo/local_step.dart'; import '../../../helpers/theme.dart'; 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'; import '../widgets/local_text.dart'; import '../widgets/progress.dart'; import '../widgets/step_widget.dart'; @@ -26,7 +24,7 @@ import 'cargo_widget.dart'; import 'carton_size_widget.dart'; import 'carton_submit.dart'; import 'model/package_selection_model.dart'; -import 'packages_widget.dart'; +import 'package_selection_widget.dart'; class CartonPackageEditor extends StatefulWidget { final Carton carton; @@ -46,7 +44,7 @@ class _CartonPackageEditorState extends State { LocalStep(lable: 'Cargos', stepType: StepType.CARGOS), LocalStep(lable: 'Submit', stepType: StepType.SUBMIT) ]; - List _packages = []; + List _cargoTypes = []; List _surchareItems = []; @@ -86,8 +84,11 @@ class _CartonPackageEditorState extends State { _billToValue = widget.carton.billTo ?? billToSender; _selectedLastMile = widget.carton.lastMile ?? delivery_caton; - _cargoTypes = widget.carton.cargoTypes; - _surchareItems = widget.carton.surchareItems; + + _cargoTypes = + widget.carton.cargoTypes.map((e) => e.cloneForCarton()).toList(); + _surchareItems = + widget.carton.surchareItems.map((e) => e.cloneForSurchage()).toList(); // check carton size type List cartonSizes = context.read().cartonSizes; @@ -120,11 +121,6 @@ class _CartonPackageEditorState extends State { .read() .getFcsShipment(widget.carton.fcsShipmentID ?? ""); _shipment = s; - - _packages = await context - .read() - .getPackagesByIds(widget.carton.packageIDs); - if (mounted) { setState(() {}); } @@ -233,19 +229,17 @@ class _CartonPackageEditorState extends State { )); } else if (step.stepType == StepType.PACKAGES) { return Expanded( - child: PackagesWidget( + child: PackageSelectionWidget( sender: _sender!, consignee: _consignee!, shipment: _shipment!, - onContinue: (packages) { + onContinue: () { setState(() { - _packages = List.from(packages); currentStep += 1; }); }, - onPrevious: (packages) { + onPrevious: () { setState(() { - _packages = List.from(packages); currentStep -= 1; }); }, @@ -288,7 +282,7 @@ class _CartonPackageEditorState extends State { height: _height, lastMile: _selectedLastMile, shipment: _shipment!, - packages: _packages, + // packages: _packages, cargoTypes: _cargoTypes, surchareItems: _surchareItems, onCreate: () { @@ -340,7 +334,7 @@ class _CartonPackageEditorState extends State { length: length, width: width, height: height, - packages: _packages, + // packages: _packages, cargoTypes: _cargoTypes, surchareItems: _surchareItems); diff --git a/lib/pages/carton/carton_package_form.dart b/lib/pages/carton/carton_package_form.dart index ce911ed..5f87927 100644 --- a/lib/pages/carton/carton_package_form.dart +++ b/lib/pages/carton/carton_package_form.dart @@ -12,7 +12,6 @@ import '../../../domain/entities/fcs_shipment.dart'; import '../../../domain/vo/local_step.dart'; import '../../../helpers/theme.dart'; import '../../domain/entities/cargo_type.dart'; -import '../../domain/entities/package.dart'; import '../../domain/entities/user.dart'; import '../main/util.dart'; import '../widgets/local_text.dart'; @@ -23,7 +22,7 @@ import 'carton_size_widget.dart'; import 'carton_submit.dart'; import 'model/carton_model.dart'; import 'model/package_selection_model.dart'; -import 'packages_widget.dart'; +import 'package_selection_widget.dart'; class CartonPackageForm extends StatefulWidget { final User sender; @@ -48,7 +47,7 @@ class _CartonPackageFormState extends State { LocalStep(lable: 'Cargos', stepType: StepType.CARGOS), LocalStep(lable: 'Submit', stepType: StepType.SUBMIT) ]; - List _packages = []; + List _cargoTypes = []; List _surchareItems = []; @@ -166,19 +165,17 @@ class _CartonPackageFormState extends State { )); } else if (step.stepType == StepType.PACKAGES) { return Expanded( - child: PackagesWidget( + child: PackageSelectionWidget( sender: widget.sender, consignee: widget.consignee, shipment: _shipment!, - onContinue: (packages) { + onContinue: () { setState(() { - _packages = List.from(packages); currentStep += 1; }); }, - onPrevious: (packages) { + onPrevious: () { setState(() { - _packages = List.from(packages); currentStep -= 1; }); }, @@ -220,7 +217,6 @@ class _CartonPackageFormState extends State { height: _height, lastMile: _selectedLastMile, shipment: _shipment!, - packages: _packages, cargoTypes: _cargoTypes, surchareItems: _surchareItems, onCreate: () { @@ -236,7 +232,6 @@ class _CartonPackageFormState extends State { } } - _create() async { setState(() { _isLoading = true; @@ -272,7 +267,7 @@ class _CartonPackageFormState extends State { length: length, width: width, height: height, - packages: _packages, + // packages: _packages, cargoTypes: _cargoTypes, surchareItems: _surchareItems); var c = await context.read().createCarton(carton); diff --git a/lib/pages/carton/carton_submit.dart b/lib/pages/carton/carton_submit.dart index 2ac3efc..d33f1ab 100644 --- a/lib/pages/carton/carton_submit.dart +++ b/lib/pages/carton/carton_submit.dart @@ -7,8 +7,6 @@ import '../../../domain/entities/cargo_type.dart'; import '../../../domain/entities/carton_size.dart'; import '../../../domain/entities/fcs_shipment.dart'; import '../../../helpers/theme.dart'; - -import '../../domain/entities/package.dart'; import '../../domain/entities/user.dart'; import '../main/util.dart'; import '../widgets/local_text.dart'; @@ -24,7 +22,7 @@ class CartonSubmit extends StatelessWidget { final User consingee; final String billToValue; final FcsShipment shipment; - final List packages; + // final List packages; final String cartonSizeType; final CartonSize? standardSize; final double length; @@ -44,7 +42,7 @@ class CartonSubmit extends StatelessWidget { this.onCreate, this.onPrevious, required this.shipment, - this.packages = const [], + // this.packages = const [], this.standardSize, required this.cartonSizeType, required this.lastMile, @@ -196,42 +194,42 @@ class CartonSubmit extends StatelessWidget { ), ); - final packagesBox = Padding( - padding: const EdgeInsets.only(top: 10), - child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Padding( - padding: const EdgeInsets.only(left: 5, bottom: 5), - child: LocalText(context, 'box.package.count', - translationVariables: [packages.length.toString()], - color: primaryColor, - fontSize: 16, - fontWeight: FontWeight.normal), - ), - Container( - decoration: BoxDecoration( - border: Border.all(color: primaryColor), - borderRadius: BorderRadius.circular(5), - ), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Wrap( - spacing: 15, - children: packages.map((e) { - return SizedBox( - width: MediaQuery.of(context).size.width / 2.5, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 3), - child: Text( - e.trackingID ?? "", - style: TextStyle(color: Colors.black, fontSize: 15), - ), - ), - ); - }).toList()), - ), - ), - ]), - ); + // final packagesBox = Padding( + // padding: const EdgeInsets.only(top: 10), + // child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + // Padding( + // padding: const EdgeInsets.only(left: 5, bottom: 5), + // child: LocalText(context, 'box.package.count', + // translationVariables: [packages.length.toString()], + // color: primaryColor, + // fontSize: 16, + // fontWeight: FontWeight.normal), + // ), + // Container( + // decoration: BoxDecoration( + // border: Border.all(color: primaryColor), + // borderRadius: BorderRadius.circular(5), + // ), + // child: Padding( + // padding: const EdgeInsets.all(8.0), + // child: Wrap( + // spacing: 15, + // children: packages.map((e) { + // return SizedBox( + // width: MediaQuery.of(context).size.width / 2.5, + // child: Padding( + // padding: const EdgeInsets.symmetric(vertical: 3), + // child: Text( + // e.trackingID ?? "", + // style: TextStyle(color: Colors.black, fontSize: 15), + // ), + // ), + // ); + // }).toList()), + // ), + // ), + // ]), + // ); final cargosBox = Padding( padding: const EdgeInsets.only(top: 10), @@ -245,7 +243,8 @@ class CartonSubmit extends StatelessWidget { color: primaryColor, fontSize: 16, fontWeight: FontWeight.normal), - Text("${removeTrailingZeros(totalWeight)} lb", + Text( + "${twoDecimalFormatted(double.tryParse(removeTrailingZeros(totalWeight)) ?? 0)} lb", style: TextStyle(color: Colors.black, fontSize: 15)) ], ), @@ -264,37 +263,121 @@ class CartonSubmit extends StatelessWidget { children: cargoTypes.map((e) { return Padding( padding: const EdgeInsets.symmetric(vertical: 3), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - e.name ?? "", - style: - TextStyle(color: Colors.black, fontSize: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + e.name ?? "", + style: TextStyle( + color: Colors.black, fontSize: 15), + ), + Text( + "${twoDecimalFormatted(double.tryParse(removeTrailingZeros(e.weight)) ?? 0)} lb", + style: TextStyle( + color: Colors.black, fontSize: 15)) + ], ), - Text("${removeTrailingZeros(e.weight)} lb", - style: TextStyle( - color: Colors.black, fontSize: 15)) + e.isMixCargo + ? Padding( + padding: const EdgeInsets.only(left: 20), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: e.mixCargoes.map((c) { + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 2), + child: Text( + "- ${c.name}", + style: TextStyle( + fontSize: 14, + color: labelColor), + ), + ); + }).toList()), + ) + : const SizedBox() ], ), ); }).toList()), - const SizedBox(height: 10), + // const SizedBox(height: 10), + // 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: labelColor, fontSize: 15), + // ), + // Text("${numberFormatter.format(e.qty)} pc", + // style: + // TextStyle(color: labelColor, fontSize: 15)) + // ], + // ), + // ); + // }).toList()), + ], + ), + ), + ), + ]), + ); + + final surChargeItemsBox = Padding( + padding: const EdgeInsets.only(top: 10), + child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + Padding( + padding: const EdgeInsets.only(left: 5, bottom: 5, right: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocalText(context, 'box.input_surcharge_item', + color: primaryColor, + fontSize: 16, + fontWeight: FontWeight.normal), + ], + ), + ), + Container( + decoration: BoxDecoration( + border: Border.all(color: primaryColor), + borderRadius: BorderRadius.circular(5), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: surchareItems.map((e) { return Padding( padding: const EdgeInsets.symmetric(vertical: 3), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - e.name ?? "", - style: TextStyle(color: labelColor, fontSize: 15), + 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("${numberFormatter.format(e.qty)} pc", - style: - TextStyle(color: labelColor, fontSize: 15)) ], ), ); @@ -365,9 +448,11 @@ class CartonSubmit extends StatelessWidget { const SizedBox(height: 10), shipmentBox, const SizedBox(height: 10), - packages.isNotEmpty ? packagesBox : const SizedBox(), + // packages.isNotEmpty ? packagesBox : const SizedBox(), + // const SizedBox(height: 10), + cargoTypes.isNotEmpty ? cargosBox : const SizedBox(), const SizedBox(height: 10), - cargosBox, + surchareItems.isNotEmpty ? surChargeItemsBox : const SizedBox(), const SizedBox(height: 20), ], ), diff --git a/lib/pages/carton/mix_carton_detail_list.dart b/lib/pages/carton/mix_carton_detail_list.dart index 9f4b94b..cf04e28 100644 --- a/lib/pages/carton/mix_carton_detail_list.dart +++ b/lib/pages/carton/mix_carton_detail_list.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:intl/intl.dart'; +import '../main/util.dart'; import '../widgets/local_text.dart'; import 'carton_info.dart'; @@ -99,7 +100,7 @@ class MixCartonDetailList extends StatelessWidget { ), ), Text( - "${carton.cartonWeight.toStringAsFixed(2)} lb", + "${twoDecimalFormatted(carton.cartonWeight)} lb", style: TextStyle(fontSize: 14.0, color: Colors.grey), ) ], diff --git a/lib/pages/carton/model/package_selection_model.dart b/lib/pages/carton/model/package_selection_model.dart index 8e1203a..3e91adc 100644 --- a/lib/pages/carton/model/package_selection_model.dart +++ b/lib/pages/carton/model/package_selection_model.dart @@ -5,59 +5,68 @@ import 'package:logging/logging.dart'; import '../../../constants.dart'; import '../../../domain/entities/package.dart'; +import '../../../pagination/paginator_listener.dart'; import '../../main/model/base_model.dart'; class PackageSelectionModel extends BaseModel { final log = Logger("PackageSelectionModel"); - String query = ""; - List packages = []; - List selectedPackages = []; + PaginatorListener? getPackages; + List packages = []; bool isLoading = false; DocumentSnapshot? _lastDocument; bool ended = false; - Timer? t; - search(String term, - {bool imm = false, - required String shipmentId, - required String senderId, - required String consigneeId}) async { - query = term; + @override + logout() async { packages.clear(); - _lastDocument = null; - ended = false; - t?.cancel(); - t = Timer(Duration(milliseconds: imm ? 0 : 800), () async { - await refresh( - term: term, - shipmentId: shipmentId, - consigneeId: consigneeId, - senderId: senderId); - }); + getPackages?.close(); + } + + loadPackages( + {required String senderId, + required String consigneeId, + required String shipmentId}) { + if (user == null) return; + + String path = "/$packages_collection"; + Query col = FirebaseFirestore.instance + .collection(path) + .where("sender_id", isEqualTo: senderId) + .where("user_id", isEqualTo: consigneeId) + .where("fcs_shipment_id", isEqualTo: shipmentId); + Query pageQuery = FirebaseFirestore.instance + .collection(path) + .where("sender_id", isEqualTo: senderId) + .where("user_id", isEqualTo: consigneeId) + .where("fcs_shipment_id", isEqualTo: shipmentId); + + pageQuery = pageQuery.orderBy("created_date", descending: true); + getPackages?.close(); + getPackages = PaginatorListener( + col, pageQuery, (data, id) => Package.fromMap(data, id), + rowPerLoad: 30); } Future refresh( {required String shipmentId, required String senderId, - required String consigneeId, - String term = ""}) async { + required String consigneeId}) async { packages.clear(); _lastDocument = null; ended = false; await loadMoreData( - shipmentId: shipmentId, - senderId: senderId, - consigneeId: consigneeId, - term: term); + shipmentId: shipmentId, + senderId: senderId, + consigneeId: consigneeId, + ); notifyListeners(); } Future loadMoreData( {required String shipmentId, required String senderId, - required String consigneeId, - String term = ""}) async { + required String consigneeId}) async { int rowPerPage = 20; try { @@ -69,12 +78,9 @@ class PackageSelectionModel extends BaseModel { whereIn: [package_processed_status, package_packed_status]) .where("sender_id", isEqualTo: senderId) .where("user_id", isEqualTo: consigneeId) + .where("fcs_shipment_id", isEqualTo: shipmentId) .where("is_deleted", isEqualTo: false); - if (term != "") { - query = query.where("tracking_id", isEqualTo: term); - } - query = query.orderBy("created_date", descending: true); if (_lastDocument != null) { @@ -92,10 +98,6 @@ class PackageSelectionModel extends BaseModel { return p; }).toList(); - for (var p in list) { - selectedPackages.contains(p) ? p.isChecked = true : p.isChecked = false; - } - packages.addAll(list); if (list.length < rowPerPage) ended = true; notifyListeners(); @@ -106,22 +108,8 @@ class PackageSelectionModel extends BaseModel { } } - selectPackage(Package a) { - if (a.isChecked) { - selectedPackages.add(a); - } else { - selectedPackages.remove(a); - } - } - - addSelectedPackage(List list) { - selectedPackages = list; - } - clearSelection() { - selectedPackages.clear(); packages.clear(); - query = ""; } Future> getPackagesBySenderAndConsigneeId( @@ -174,4 +162,21 @@ class PackageSelectionModel extends BaseModel { } return list; } + + Future getPackageCount( + {required String senderId, + required String consigneeId, + required String shipmentId}) async { + String path = "/$packages_collection"; + + AggregateQuerySnapshot query = await FirebaseFirestore.instance + .collection(path) + .where("sender_id", isEqualTo: senderId) + .where("user_id", isEqualTo: consigneeId) + .where("fcs_shipment_id", isEqualTo: shipmentId) + .count() + .get(); + + return query.count; + } } diff --git a/lib/pages/carton/package_detail_list.dart b/lib/pages/carton/package_detail_list.dart index 4dd72f1..f3caae6 100644 --- a/lib/pages/carton/package_detail_list.dart +++ b/lib/pages/carton/package_detail_list.dart @@ -1,24 +1,54 @@ import 'package:fcs/domain/entities/package.dart'; import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/carton/model/package_selection_model.dart'; import 'package:fcs/pages/package/package_info.dart'; import 'package:fcs/pages/widgets/local_app_bar.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; +import '../../pagination/paginator_listview.dart'; import '../widgets/local_text.dart'; -class PackageDetailList extends StatelessWidget { +class PackageDetailList extends StatefulWidget { final String cartonNumber; - final List packages; - PackageDetailList( - {super.key, required this.cartonNumber, required this.packages}); + final int packageCount; + final String senderID; + final String consingeeID; + final String fcsShipmentID; + const PackageDetailList( + {super.key, + required this.cartonNumber, + required this.packageCount, + required this.senderID, + required this.consingeeID, + required this.fcsShipmentID}); + @override + State createState() => _PackageDetailListState(); +} + +class _PackageDetailListState extends State { final NumberFormat numberFormatter = NumberFormat("#,###"); + @override + void initState() { + _init(); + super.initState(); + } + + _init() { + context.read().loadPackages( + senderId: widget.senderID, + consigneeId: widget.consingeeID, + shipmentId: widget.fcsShipmentID); + } + @override Widget build(BuildContext context) { + var packageModel = context.watch(); return Scaffold( appBar: LocalAppBar( backgroundColor: Colors.white, @@ -31,81 +61,81 @@ class PackageDetailList extends StatelessWidget { "box.package.count", fontSize: 20, color: primaryColor, - translationVariables: [numberFormatter.format(packages.length)], + translationVariables: [ + numberFormatter.format(widget.packageCount) + ], ), - Text(cartonNumber, + Text(widget.cartonNumber, style: TextStyle(fontSize: 15, color: Colors.black)) ], ), ), - body: ListView.separated( - separatorBuilder: (context, index) => - Divider(height: 1, color: dividerColor), - itemCount: packages.length, - itemBuilder: (BuildContext context, int index) { - var package = packages[index]; - return InkWell( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => PackageInfo(package: package)), - ); - }, - child: Container( - padding: EdgeInsets.only(left: 15, right: 15), - child: Column( - children: [ - Row( - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Row( - children: [ - Icon(Octicons.package, color: primaryColor), - Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 15), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - package.id == null - ? '' - : package.trackingID!, - style: TextStyle( - fontSize: 15.0, - color: Colors.black), - ), - Padding( - padding: - const EdgeInsets.only(top: 5), - child: Text( - package.market == null + body: PaginatorListView( + paginatorListener: packageModel.getPackages!, + rowBuilder: (p) { + Package package = p; + return InkWell( + onTap: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => PackageInfo(package: package)), + ); + }, + child: Container( + padding: EdgeInsets.only(left: 15, right: 15), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + children: [ + Icon(Octicons.package, color: primaryColor), + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 15), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + package.id == null ? '' - : package.market!, + : package.trackingID!, style: TextStyle( fontSize: 15.0, - color: Colors.grey), + color: Colors.black), ), - ), - ], + Padding( + padding: + const EdgeInsets.only(top: 5), + child: Text( + package.market == null + ? '' + : package.market!, + style: TextStyle( + fontSize: 15.0, + color: Colors.grey), + ), + ), + ], + ), ), ), - ), - ], + ], + ), ), ), - ), - ], - ), - ], + ], + ), + ], + ), ), - ), - ); - }, - )); + ); + }, + color: primaryColor)); } } diff --git a/lib/pages/carton/package_selection_result.dart b/lib/pages/carton/package_selection_result.dart index 1beba25..b471118 100644 --- a/lib/pages/carton/package_selection_result.dart +++ b/lib/pages/carton/package_selection_result.dart @@ -74,32 +74,38 @@ class PackageSelectionResult extends StatelessWidget { child: Row( children: [ Expanded( - child: new Padding( + child: Padding( padding: const EdgeInsets.symmetric( vertical: 8.0), - child: new Column( + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - new Text(package.trackingID ?? "", - style: new TextStyle( + Text(package.trackingID ?? "", + style: TextStyle( fontSize: 15.0, color: Colors.black)), - new Text( - package.cartonIds.isEmpty - ? "-" - : "${numberFormatter.format(package.cartonIds.length)} Boxes", - style: new TextStyle( + Text( + package.market != null && + package.market != "" + ? "${package.market}" + : "-", + style: TextStyle( fontSize: 15.0, color: Colors.grey), ), + // Text( + // package.cartonIds.isEmpty + // ? "-" + // : "${numberFormatter.format(package.cartonIds.length)} Boxes", + // style: TextStyle( + // fontSize: 15.0, + // color: Colors.grey), + // ), ], ), ), ), - package.isChecked - ? Icon(Icons.check, color: primaryColor) - : const SizedBox() ], ), ), diff --git a/lib/pages/carton/package_selection_widget.dart b/lib/pages/carton/package_selection_widget.dart index 9fb40cc..40df1bd 100644 --- a/lib/pages/carton/package_selection_widget.dart +++ b/lib/pages/carton/package_selection_widget.dart @@ -1,5 +1,5 @@ import 'package:fcs/domain/entities/fcs_shipment.dart'; -import 'package:fcs/helpers/theme.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart'; @@ -8,27 +8,25 @@ import 'package:provider/provider.dart'; import '../../domain/entities/package.dart'; import '../../domain/entities/user.dart'; import '../main/util.dart'; -import '../widgets/barcode_scanner.dart'; +import '../package/package_info.dart'; import '../widgets/continue_button.dart'; import '../widgets/local_title.dart'; import '../widgets/previous_button.dart'; import 'model/package_selection_model.dart'; import 'package_selection_result.dart'; -typedef OnPrevious = Function(List packages); -typedef OnContinue = Function(List packages); +typedef OnPrevious = Function(); +typedef OnContinue = Function(); class PackageSelectionWidget extends StatefulWidget { final User sender; final User consignee; final FcsShipment shipment; - final List packages; final OnPrevious? onPrevious; final OnContinue? onContinue; const PackageSelectionWidget({ super.key, - required this.packages, this.onPrevious, this.onContinue, required this.shipment, @@ -41,8 +39,6 @@ class PackageSelectionWidget extends StatefulWidget { } class _PackageSelectionWidgetState extends State { - final TextEditingController _controller = TextEditingController(); - String _query = ""; bool _isLoadMore = false; final _scrollController = ScrollController(); bool _down = true; @@ -60,29 +56,18 @@ class _PackageSelectionWidgetState extends State { } _init() { - var searchModel = context.read(); - _controller.text = searchModel.query; - _query = searchModel.query; - - searchModel.refresh( - shipmentId: widget.shipment.id!, - consigneeId: widget.consignee.id!, - senderId: widget.sender.id!, - term: _query); - - searchModel.addSelectedPackage(widget.packages); + var packageModel = context.read(); + packageModel.refresh( + shipmentId: widget.shipment.id!, + consigneeId: widget.consignee.id!, + senderId: widget.sender.id!, + ); if (mounted) { setState(() {}); } } - // @override - // void didUpdateWidget(covariant PackageSelectionWidget oldWidget) { - // _init(); - // super.didUpdateWidget(oldWidget); - // } - Future _loadMoreData() async { if (_isLoadMore) return; var model = context.read(); @@ -92,10 +77,10 @@ class _PackageSelectionWidgetState extends State { }); await model.loadMoreData( - shipmentId: widget.shipment.id!, - consigneeId: widget.consignee.id!, - senderId: widget.sender.id!, - term: _query); + shipmentId: widget.shipment.id!, + consigneeId: widget.consignee.id!, + senderId: widget.sender.id!, + ); setState(() { _isLoadMore = false; @@ -105,8 +90,7 @@ class _PackageSelectionWidgetState extends State { @override Widget build(BuildContext context) { var model = context.watch(); - List searchResults = model.packages; - List selectedPackageList = model.selectedPackages; + List packages = model.packages; final senderBox = userDisplayBox(context, lableKey: "box.sender.title", @@ -132,103 +116,23 @@ class _PackageSelectionWidgetState extends State { final continueBtn = ContinueButton( onTap: () { - if (selectedPackageList.isEmpty || searchResults.isEmpty) { - showMsgDialog(context, 'Error', "Please select the packages"); + if (packages.isEmpty) { + showMsgDialog(context, 'Error', "Please add the packages"); return false; } if (widget.onContinue != null) { - widget.onContinue!(selectedPackageList); + widget.onContinue!(); } }, ); final previousBtn = PreviousButton(onTap: () { if (widget.onPrevious != null) { - widget.onPrevious!(selectedPackageList); + widget.onPrevious!(); } }); - final searchBox = SizedBox( - height: 40, - child: TextField( - controller: _controller, - cursorColor: primaryColor, - onSubmitted: (value) { - setState(() { - _query = value; - }); - _search(imm: true); - }, - onChanged: (v) { - setState(() { - _query = v; - }); - _search(); - }, - decoration: InputDecoration( - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: labelColor), - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(5.0), - borderSide: - BorderSide(color: labelColor.withOpacity(0.3), width: 0)), - focusedBorder: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(5.0)), - borderSide: BorderSide(color: labelColor), - ), - hintText: "Search by tracking number", - hintStyle: const TextStyle( - color: Colors.grey, - fontSize: 16, - fontWeight: FontWeight.normal), - suffixIcon: Row( - mainAxisSize: MainAxisSize.min, - children: [ - InkResponse( - radius: 20, - onTap: () { - _scan(context); - }, - child: const Icon(Icons.qr_code_scanner, - color: Colors.black87)), - IconButton( - splashRadius: 20, - onPressed: () { - setState(() { - _controller.clear(); - _query = ""; - }); - _search(); - }, - icon: const Icon(Icons.close, color: Colors.black87)), - ], - ), - contentPadding: const EdgeInsets.all(10), - filled: true), - ), - ); - - final selectedPackageBox = Align( - alignment: Alignment.topLeft, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - physics: const BouncingScrollPhysics(), - padding: EdgeInsets.only(top: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: selectedPackageList - .map((e) => Padding( - padding: const EdgeInsets.only(right: 20), - child: Text(e.trackingID ?? "", - style: - TextStyle(color: Colors.black, fontSize: 15)), - )) - .toList())), - ); return Column( children: [ Expanded( @@ -255,8 +159,6 @@ class _PackageSelectionWidgetState extends State { LocalTitle( textKey: "box.select.package", topPadding: 5), const SizedBox(height: 10), - searchBox, - selectedPackageBox, ]) : const SizedBox(), ), @@ -273,11 +175,12 @@ class _PackageSelectionWidgetState extends State { _down = true; }); }, - onTap: (a) async { - setState(() { - a.isChecked = !a.isChecked; - }); - context.read().selectPackage(a); + onTap: (a) { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => PackageInfo(package: a)), + ); }, ), ), @@ -302,31 +205,4 @@ class _PackageSelectionWidgetState extends State { ], ); } - - _scan(BuildContext context) async { - try { - String? barcode = await scanBarcode(); - if (barcode != null) { - setState(() { - _controller.text = barcode; - _query = barcode; - }); - await _search(); - } - } catch (e) { - showMsgDialog(context, 'Error', e.toString()); - } - } - - _search({bool imm = false}) async { - try { - await context.read().search(_query, - imm: imm, - shipmentId: widget.shipment.id!, - senderId: widget.sender.id!, - consigneeId: widget.consignee.id!); - } catch (e) { - showMsgDialog(context, 'Error', e.toString()); - } - } } diff --git a/lib/pages/carton/packages_widget.dart b/lib/pages/carton/packages_widget.dart deleted file mode 100644 index 69933dc..0000000 --- a/lib/pages/carton/packages_widget.dart +++ /dev/null @@ -1,223 +0,0 @@ -import 'package:fcs/domain/entities/fcs_shipment.dart'; -import 'package:fcs/helpers/theme.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart'; -import 'package:provider/provider.dart'; - -import '../../domain/entities/package.dart'; -import '../../domain/entities/user.dart'; -import '../main/util.dart'; -import '../widgets/continue_button.dart'; -import '../widgets/local_text.dart'; -import '../widgets/local_title.dart'; -import '../widgets/previous_button.dart'; -import 'model/package_selection_model.dart'; -import 'package_selection_result.dart'; - -typedef OnPrevious = Function(List packages); -typedef OnContinue = Function(List packages); - -class PackagesWidget extends StatefulWidget { - final User sender; - final User consignee; - final FcsShipment shipment; - final OnPrevious? onPrevious; - final OnContinue? onContinue; - - const PackagesWidget({ - super.key, - this.onPrevious, - this.onContinue, - required this.shipment, - required this.sender, - required this.consignee, - }); - - @override - State createState() => _PackagesWidgetState(); -} - -class _PackagesWidgetState extends State { - final _scrollController = ScrollController(); - bool _down = true; - List _packages = []; - bool _isLoading = false; - - @override - void initState() { - _init(); - super.initState(); - _scrollController.addListener(() { - setState(() { - _down = _scrollController.position.userScrollDirection == - ScrollDirection.forward; - }); - }); - } - - _init() async { - _packages.clear(); - _isLoading = true; - var packageModel = context.read(); - var list = await packageModel.getActivePackages( - shipmentId: widget.shipment.id!, - senderId: widget.sender.id!, - consigneeId: widget.consignee.id!); - _packages = List.from(list); - _isLoading = false; - if (mounted) { - setState(() {}); - } - } - - @override - Widget build(BuildContext context) { - final senderBox = userDisplayBox(context, - lableKey: "box.sender.title", - icon: MaterialCommunityIcons.account_arrow_right, - showLink: false, - name: widget.sender.name ?? "", - fcsID: widget.sender.fcsID ?? ""); - - final consigneeBox = userDisplayBox(context, - showLink: false, - lableKey: "box.consignee.title", - icon: MaterialCommunityIcons.account_arrow_left, - name: widget.consignee.name, - fcsID: widget.consignee.fcsID); - - final userRow = Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded(flex: 2, child: consigneeBox), - Flexible(child: senderBox) - ], - ); - - final continueBtn = ContinueButton( - onTap: () { - if (_packages.isEmpty) { - showMsgDialog(context, 'Error', "Please add the packages"); - return false; - } - if (widget.onContinue != null) { - widget.onContinue!(_packages); - } - }, - ); - - final previousBtn = PreviousButton(onTap: () { - if (widget.onPrevious != null) { - widget.onPrevious!(_packages); - } - }); - - return Column( - children: [ - Expanded( - child: Padding( - padding: EdgeInsets.only(left: 10, right: 10), - child: Column( - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - transitionBuilder: - (Widget child, Animation animation) => - FadeTransition( - opacity: animation, - child: SizeTransition( - sizeFactor: animation, - axis: Axis.vertical, - child: child, - ), - ), - child: _down - ? Column(children: [ - const SizedBox(height: 8), - userRow, - LocalTitle( - textKey: "box.select.package", topPadding: 5), - const SizedBox(height: 10), - ]) - : const SizedBox(), - ), - Expanded( - child: _packages.isEmpty && !_isLoading - ? Center( - child: LocalText(context, 'box.no_package', - color: Colors.black, fontSize: 15)) - : RefreshIndicator( - color: primaryColor, - onRefresh: () async { - _init(); - }, - child: ListView.builder( - padding: const EdgeInsets.only(top: 10), - controller: _scrollController, - shrinkWrap: true, - physics: const AlwaysScrollableScrollPhysics(), - itemBuilder: (context, index) { - Package package = _packages[index]; - return packageRow(context, package); - }, - itemCount: _packages.length)), - ), - ], - ), - ), - ), - widget.onContinue != null - ? Padding( - padding: const EdgeInsets.only(left: 15, right: 15, top: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - previousBtn, - continueBtn, - ], - ), - ) - : const SizedBox(), - const SizedBox(height: 20) - ], - ); - } - - Widget packageRow(BuildContext context, Package package) { - return Padding( - padding: const EdgeInsets.only(top: 5, bottom: 5), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(5)), - border: Border.all(color: Colors.grey.shade300)), - padding: EdgeInsets.only(left: 10, right: 10), - child: Row( - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(package.trackingID ?? "", - style: TextStyle(fontSize: 15.0, color: Colors.black)), - Text( - package.cartonIds.isEmpty - ? "-" - : "${numberFormatter.format(package.cartonIds.length)} Boxes", - style: TextStyle(fontSize: 15.0, color: Colors.grey), - ), - ], - ), - ), - ), - package.isChecked - ? Icon(Icons.check, color: primaryColor) - : const SizedBox() - ], - ), - ), - ); - } -} diff --git a/lib/pages/carton/widget/carton_list_row.dart b/lib/pages/carton/widget/carton_list_row.dart index 56c1a4a..9d24565 100644 --- a/lib/pages/carton/widget/carton_list_row.dart +++ b/lib/pages/carton/widget/carton_list_row.dart @@ -4,15 +4,16 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:intl/intl.dart'; +import '../../main/util.dart'; import '../carton_info.dart'; import '../print_qr_code_page.dart'; class CartonListRow extends StatelessWidget { final Carton box; - CartonListRow({Key? key, required this.box}) : super(key: key); + CartonListRow({super.key, required this.box}); final double dotSize = 15.0; - final DateFormat dateFormat = new DateFormat("dd MMM yyyy"); + final DateFormat dateFormat = DateFormat("dd MMM yyyy"); @override Widget build(BuildContext context) { @@ -28,9 +29,9 @@ class CartonListRow extends StatelessWidget { child: Row( children: [ Expanded( - child: new Padding( + child: Padding( padding: const EdgeInsets.symmetric(vertical: 10.0), - child: new Row( + child: Row( children: [ Container( padding: EdgeInsets.only(left: 0), @@ -40,24 +41,24 @@ class CartonListRow extends StatelessWidget { size: 30, ), ), - new Expanded( + Expanded( child: Padding( padding: const EdgeInsets.only(left: 15.0), child: Row( children: [ - new Column( + Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - new Text( + Text( box.cartonNumber ?? "", - style: new TextStyle( + style: TextStyle( fontSize: 15.0, color: Colors.black), ), Padding( padding: const EdgeInsets.only(top: 5), - child: new Text( + child: Text( box.consigneeName ?? "", - style: new TextStyle( + style: TextStyle( fontSize: 15.0, color: Colors.grey), ), ), @@ -95,10 +96,9 @@ class CartonListRow extends StatelessWidget { padding: const EdgeInsets.only(top: 5), child: Row( children: [ - new Text( - "${box.cartonWeight.toStringAsFixed(2)} lb", - style: - new TextStyle(fontSize: 14.0, color: Colors.grey), + Text( + "${twoDecimalFormatted(box.cartonWeight)} lb", + style: TextStyle(fontSize: 14.0, color: Colors.grey), ), ], ), diff --git a/lib/pages/main/util.dart b/lib/pages/main/util.dart index 98e1837..e529ac6 100644 --- a/lib/pages/main/util.dart +++ b/lib/pages/main/util.dart @@ -410,6 +410,11 @@ String removeTrailingZeros(double number) { return result; } +String twoDecimalFormatted(double number) { + String formattedString = number.toStringAsFixed(2); + return formattedString; +} + bool isValidEmail(String email) { // Define a regular expression for validating an email final emailRegex = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$'); @@ -561,4 +566,3 @@ Widget settingRow(BuildContext context, ), ); } -