diff --git a/.gitignore b/.gitignore index 2adc0e1..60ad1dd 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,11 @@ android/key.jks android/app/src/prod/AndroidManifest.xml android/app/src/prod/google-services.json android/app/src/prod/res/values/strings.xml +.gradle/5.6.2/gc.properties +.gradle/5.6.2/executionHistory/executionHistory.bin +.gradle/5.6.2/executionHistory/executionHistory.lock +.gradle/5.6.2/fileChanges/last-build.bin +.gradle/5.6.2/fileHashes/fileHashes.lock +.gradle/buildOutputCleanup/buildOutputCleanup.lock +.gradle/buildOutputCleanup/cache.properties +.gradle/vcs-1/gc.properties diff --git a/assets/local/localization_en.json b/assets/local/localization_en.json index 4365bd3..c3fa161 100644 --- a/assets/local/localization_en.json +++ b/assets/local/localization_en.json @@ -287,6 +287,11 @@ "box.cargo.total":"Total Weight", "box.delete.confirm":"Delete carton?", "box.mix.carton":"Go into mix carton", + "box.carton_size":"Carton Size", + "box.carton_size.title":"Carton Sizes", + "box.carton_size_remove.confirm":"Remove this carton size?", + "box.carton_size.name":"Name", + "box.cargo_total_title":"Edit Total Weight", "Boxes End ================================================================":"", "Delivery Start ================================================================":"", @@ -539,5 +544,14 @@ "processing.delete.confirm":"Delete this package?", "processing.edit.sub_title":"Processing", "processing.edit.complete.btn":"Complete processing", + "processing.new":"New Processing", + "processing.create":"New Processing", + "processing.update":"Update Processing", + "processing.consignee.name":"Consignee name", + "processing.shipper.name":"Shipper name", + "processing.package.select.btn":"Select", + "processing.package.create":"New Package", + "processing.package.update":"Update Package", + "processing.package.delete_confirm":"Remove this package?", "Processing End ================================================================":"" } \ No newline at end of file diff --git a/assets/local/localization_mu.json b/assets/local/localization_mu.json index 141d669..91dd517 100644 --- a/assets/local/localization_mu.json +++ b/assets/local/localization_mu.json @@ -287,6 +287,11 @@ "box.cargo.total":"စုစုပေါင်း အလေးချိန်", "box.delete.confirm":"Delete carton?", "box.mix.carton":"Go into mix carton", + "box.carton_size":"သေတ္တာ အရွယ်အစား", + "box.carton_size.title":"သေတ္တာ အရွယ်အစားများ", + "box.carton_size_remove.confirm":"Remove this carton size?", + "box.carton_size.name":"နာမည်", + "box.cargo_total_title":"စုစုပေါင်းအလေးချိန် ပြုပြင်မည်", "Boxes End ================================================================":"", "Delivery Start ================================================================":"", @@ -540,6 +545,15 @@ "processing.delete.confirm":"အထုပ်ကို ဖျက်မလား?", "processing.edit.sub_title":"မွမ်းမံခြင်း", "processing.edit.complete.btn":"မွမ်းမံခြင်း ပြီးဆုံးသည်", + "processing.new":"လုပ်ဆောင်ခြင်း အသစ်", + "processing.create":"လုပ်ဆောင်ခြင်း အသစ်", + "processing.update":"လုပ်ဆောင်ခြင်း ပြင်ဆင်ခြင်း", + "processing.consignee.name":"လက်ခံသူ အမည်", + "processing.shipper.name":"တင်ပို့သူ အမည်", + "processing.package.select.btn":"ရွေးချယ်မည်", + "processing.package.create":"အထုပ် အသစ်", + "processing.package.update":"အထုပ် ပြင်ဆင်ခြင်း ", + "processing.package.delete_confirm":"Remove this package?", "Processing End ================================================================":"" diff --git a/lib/app.dart b/lib/app.dart index ff298d5..fcd492e 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,6 +1,7 @@ import 'package:fcs/localization/app_translations_delegate.dart'; import 'package:fcs/localization/transalation.dart'; import 'package:fcs/pages/carton/model/carton_model.dart'; +import 'package:fcs/pages/carton_size/model/carton_size_model.dart'; import 'package:fcs/pages/chat/model/message_model.dart'; import 'package:fcs/pages/contact/model/contact_model.dart'; import 'package:fcs/pages/customer/model/customer_model.dart'; @@ -16,6 +17,7 @@ import 'package:fcs/pages/main/model/language_model.dart'; import 'package:fcs/pages/main/model/main_model.dart'; import 'package:fcs/pages/package/model/package_model.dart'; import 'package:fcs/pages/payment_methods/model/payment_method_model.dart'; +import 'package:fcs/pages/processing/model/processing_model.dart'; import 'package:fcs/pages/rates/model/shipment_rate_model.dart'; import 'package:fcs/pages/shipment/model/shipment_model.dart'; import 'package:fcs/pages/main/splash_page.dart'; @@ -54,6 +56,8 @@ class _AppState extends State { final PackageModel packageModel = new PackageModel(); final MarketModel marketModel = new MarketModel(); final DeliveryModel deliveryModel = new DeliveryModel(); + final CartonSizeModel cartonSizeModel = new CartonSizeModel(); + final ProcessingModel processingModel = new ProcessingModel(); AppTranslationsDelegate _newLocaleDelegate; @@ -73,7 +77,9 @@ class _AppState extends State { ..addModel(shipmentModel) ..addModel(invoiceModel) ..addModel(marketModel) - ..addModel(deliveryModel); + ..addModel(deliveryModel) + ..addModel(cartonSizeModel) + ..addModel(processingModel); _newLocaleDelegate = AppTranslationsDelegate(newLocale: null); Translation().onLocaleChanged = onLocaleChange; @@ -118,6 +124,8 @@ class _AppState extends State { ChangeNotifierProvider.value(value: marketModel), ChangeNotifierProvider.value(value: fcsShipmentModel), ChangeNotifierProvider.value(value: deliveryModel), + ChangeNotifierProvider.value(value: cartonSizeModel), + ChangeNotifierProvider.value(value: processingModel), ], child: Consumer( builder: (context, value, child) { diff --git a/lib/domain/entities/cargo_type.dart b/lib/domain/entities/cargo_type.dart index 7247ce0..6534d71 100644 --- a/lib/domain/entities/cargo_type.dart +++ b/lib/domain/entities/cargo_type.dart @@ -3,6 +3,7 @@ class CargoType { String name; double rate; double weight; + bool isChecked; double get calAmount => (calRate ?? 0) * (calWeight ?? 0); @@ -19,13 +20,15 @@ class CargoType { calRate: map['cal_rate']?.toDouble() ?? 0, ); } - CargoType( - {this.id, - this.name, - this.rate, - this.weight, - this.calWeight, - this.calRate}); + CargoType({ + this.id, + this.name, + this.rate, + this.weight, + this.calWeight, + this.calRate, + this.isChecked = false, + }); Map toMap() { return { diff --git a/lib/domain/entities/carton_size.dart b/lib/domain/entities/carton_size.dart new file mode 100644 index 0000000..48c4ed9 --- /dev/null +++ b/lib/domain/entities/carton_size.dart @@ -0,0 +1,8 @@ +class CartonSize { + String id; + String name; + double length; + double width; + double height; + CartonSize({this.id, this.name, this.length, this.width, this.height}); +} diff --git a/lib/domain/entities/package.dart b/lib/domain/entities/package.dart index 70f9af2..e03c814 100644 --- a/lib/domain/entities/package.dart +++ b/lib/domain/entities/package.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:fcs/domain/vo/delivery_address.dart'; import 'package:fcs/domain/vo/shipment_status.dart'; @@ -36,42 +38,47 @@ class Package { DateTime arrivedDate; DeliveryAddress deliveryAddress; + //for packages in processing + List photoFiles; + int get amount => rate != null && weight != null ? rate * weight : 0; String get packageNumber => shipmentNumber + "-" + receiverNumber + " #" + boxNumber; double get price => rate.toDouble() * weight; - Package( - {this.id, - this.trackingID, - this.userID, - this.userName, - this.fcsID, - this.phoneNumber, - this.shipmentNumber, - this.senderFCSID, - this.senderName, - this.receiverFCSID, - this.receiverName, - this.receiverNumber, - this.receiverAddress, - this.boxNumber, - this.rate, - this.weight, - this.packageType, - this.pickUpID, - this.remark, - this.status, - this.arrivedDate, - this.cargoDesc, - this.market, - this.shipmentHistory, - this.currentStatusDate, - this.photoUrls, - this.desc, - this.deliveryAddress, - this.isChecked = false}); + Package({ + this.id, + this.trackingID, + this.userID, + this.userName, + this.fcsID, + this.phoneNumber, + this.shipmentNumber, + this.senderFCSID, + this.senderName, + this.receiverFCSID, + this.receiverName, + this.receiverNumber, + this.receiverAddress, + this.boxNumber, + this.rate, + this.weight, + this.packageType, + this.pickUpID, + this.remark, + this.status, + this.arrivedDate, + this.cargoDesc, + this.market, + this.shipmentHistory, + this.currentStatusDate, + this.photoUrls, + this.desc, + this.deliveryAddress, + this.isChecked = false, + this.photoFiles, + }); factory Package.fromMap(Map map, String docID) { var _currentStatusDate = (map['status_date'] as Timestamp); diff --git a/lib/domain/entities/processing.dart b/lib/domain/entities/processing.dart new file mode 100644 index 0000000..f801903 --- /dev/null +++ b/lib/domain/entities/processing.dart @@ -0,0 +1,36 @@ +import 'package:fcs/domain/entities/package.dart'; + +class Processing { + String id; + //for consignee + String userID; + String userName; + String userPhoneNumber; + //for shipper + String fcsID; + String shipperName; + String shipperPhoneNumber; + + List packages; + + Processing( + {this.id, + this.userID, + this.userName, + this.userPhoneNumber, + this.fcsID, + this.shipperName, + this.shipperPhoneNumber, + this.packages}); + + @override + bool operator ==(Object other) => other is Processing && other.id == id; + + @override + int get hashCode => id.hashCode; + + @override + String toString() { + return 'Processing{id: $id}'; + } +} diff --git a/lib/helpers/theme.dart b/lib/helpers/theme.dart index c52f8ba..e029208 100644 --- a/lib/helpers/theme.dart +++ b/lib/helpers/theme.dart @@ -11,10 +11,10 @@ const buttonBkColor = const Color(0xFF268944); const labelColor = const Color(0xFF757575); const TextStyle labelStyle = - TextStyle(fontSize: 20, color: primaryColor, fontWeight: FontWeight.w500); + TextStyle(fontSize: 20, color: labelColor, fontWeight: FontWeight.w500); const TextStyle labelStyleMM = TextStyle( fontSize: 20, - color: primaryColor, + color: labelColor, fontWeight: FontWeight.w500, height: 1, fontFamily: "Myanmar3"); diff --git a/lib/pages/carton/cargo_type_addtion.dart b/lib/pages/carton/cargo_type_addtion.dart new file mode 100644 index 0000000..64dff27 --- /dev/null +++ b/lib/pages/carton/cargo_type_addtion.dart @@ -0,0 +1,116 @@ +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_text.dart'; +import 'package:fcs/pages/widgets/progress.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class CargoTypeAddition extends StatefulWidget { + @override + _CargoTypeAdditionState createState() => _CargoTypeAdditionState(); +} + +class _CargoTypeAdditionState extends State { + bool _isLoading = false; + + List cargos = []; + + @override + void initState() { + super.initState(); + cargos = + Provider.of(context, listen: false).rate.cargoTypes; + cargos.forEach((p) => p.isChecked = false); + } + + @override + void dispose() { + super.dispose(); + } + + List showcargoTypeList(BuildContext context) { + return this.cargos.map((c) { + return new ListTile( + contentPadding: EdgeInsets.all(0), + title: InkWell( + onTap: () { + setState(() { + c.isChecked = c.isChecked == null ? true : !c.isChecked; + }); + }, + child: new Row( + children: [ + new Checkbox( + value: c.isChecked == null ? false : c.isChecked, + activeColor: primaryColor, + onChanged: (bool value) { + setState(() { + c.isChecked = value; + }); + }), + new Text( + c.name, + style: TextStyle(fontSize: 15.0, color: primaryColor), + ), + ], + ), + )); + }).toList(); + } + + @override + Widget build(BuildContext context) { + final saveBtn = fcsButton( + context, + getLocalString(context, 'box.cargo.save.btn'), + callack: () { + this.cargos.forEach((p) => p.weight = 0.00); + List _cargos = + this.cargos.where((c) => c.isChecked).toList(); + Navigator.pop(context, _cargos); + }, + ); + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: + new Icon(CupertinoIcons.back, color: primaryColor, size: 30), + onPressed: () => Navigator.of(context).pop(), + ), + shadowColor: Colors.transparent, + backgroundColor: Colors.white, + title: LocalText( + context, + "cargo.form.title", + fontSize: 20, + color: primaryColor, + )), + body: Container( + padding: EdgeInsets.all(18), + child: ListView( + shrinkWrap: true, + children: [ + Column( + children: showcargoTypeList(context), + ), + SizedBox(height: 30), + saveBtn, + SizedBox(height: 20), + ], + ), + ), + ), + ); + } + + // List cargoTypeList() { + // return this.privileges.where((p) => p.isChecked).map((p) => p.id).toList(); + // } + +} diff --git a/lib/pages/carton/cargo_type_editor.dart b/lib/pages/carton/cargo_type_editor.dart index b73f73b..78c4b32 100644 --- a/lib/pages/carton/cargo_type_editor.dart +++ b/lib/pages/carton/cargo_type_editor.dart @@ -31,7 +31,7 @@ class _CargoTypeEditorState extends State { super.initState(); if (widget.cargo != null) { _cargo = widget.cargo; - _weightController.text = _cargo.weight.toString(); + _weightController.text = _cargo.weight.toStringAsFixed(2); } else { _loadDefalut(); } diff --git a/lib/pages/carton/carton_cargo_table.dart b/lib/pages/carton/carton_cargo_table.dart index 3b49332..8778547 100644 --- a/lib/pages/carton/carton_cargo_table.dart +++ b/lib/pages/carton/carton_cargo_table.dart @@ -6,11 +6,12 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'cargo_type_editor.dart'; +import 'total_weight_edit.dart'; typedef OnAdd(CargoType cargoType); typedef OnRemove(CargoType cargoType); -class CargoTable extends StatelessWidget { +class CargoTable extends StatefulWidget { final List cargoTypes; final OnAdd onAdd; final OnRemove onRemove; @@ -18,6 +19,12 @@ class CargoTable extends StatelessWidget { const CargoTable({Key key, this.cargoTypes, this.onAdd, this.onRemove}) : super(key: key); + @override + _CargoTableState createState() => _CargoTableState(); +} + +class _CargoTableState extends State { + double totalWeight = 0; @override Widget build(BuildContext context) { return MyDataTable( @@ -43,12 +50,14 @@ class CargoTable extends StatelessWidget { } List getCargoRows(BuildContext context) { - if (cargoTypes == null) { + if (widget.cargoTypes == null) { return []; } - double total = 0; - var rows = cargoTypes.map((c) { - total += c.weight; + + double _total = 0; + var rows = widget.cargoTypes.map((c) { + _total += c.weight; + return MyDataRow( onSelectChanged: (bool selected) async { CargoType cargo = await Navigator.push( @@ -57,7 +66,7 @@ class CargoTable extends StatelessWidget { builder: (context) => CargoTypeEditor( cargo: c, ))); - if (onAdd != null) onAdd(cargo); + if (widget.onAdd != null) widget.onAdd(cargo); }, cells: [ MyDataCell(new Text( @@ -68,9 +77,8 @@ class CargoTable extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - Text(c.weight == null ? "0" : c.weight.toString(), - style: textStyle), - onRemove == null + Text(c.weight.toStringAsFixed(2), style: textStyle), + widget.onRemove == null ? SizedBox( width: 50, ) @@ -80,7 +88,93 @@ class CargoTable extends StatelessWidget { color: primaryColor, ), onPressed: () { - if (onRemove != null) onRemove(c); + if (widget.onRemove != null) widget.onRemove(c); + }) + ], + ), + ), + ], + ); + }).toList(); + + var totalRow = MyDataRow( + onSelectChanged: (bool selected) async { + double _t = await Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => TotalWeightEdit(totalWeight: totalWeight))); + if (_t == null) return; + setState(() { + totalWeight = _t; + }); + }, + cells: [ + MyDataCell(Align( + alignment: Alignment.centerRight, + child: LocalText( + context, + "shipment.cargo.total", + color: Colors.black87, + fontWeight: FontWeight.bold, + ), + )), + MyDataCell( + Padding( + padding: const EdgeInsets.only(right: 48.0), + child: Align( + alignment: Alignment.centerRight, + child: Text(totalWeight.toStringAsFixed(2), + style: TextStyle(fontWeight: FontWeight.bold))), + ), + ), + ], + ); + rows.add(totalRow); + return rows; + } + + double getRemainBalance(double total) { + double _r = this.totalWeight < total ? 0 : this.totalWeight - total; + return _r; + } + + List _getCargoRows(BuildContext context) { + if (widget.cargoTypes == null) { + return []; + } + double total = 0; + var rows = widget.cargoTypes.map((c) { + total += c.weight; + return MyDataRow( + onSelectChanged: (bool selected) async { + CargoType cargo = await Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => CargoTypeEditor( + cargo: c, + ))); + if (widget.onAdd != null) widget.onAdd(cargo); + }, + cells: [ + MyDataCell(new Text( + c.name == null ? "" : c.name, + style: textStyle, + )), + MyDataCell( + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text(c.weight == null ? "0" : c.weight.toStringAsFixed(2), + style: textStyle), + widget.onRemove == null + ? SizedBox( + width: 50, + ) + : IconButton( + icon: Icon( + Icons.remove_circle, + color: primaryColor, + ), + onPressed: () { + if (widget.onRemove != null) widget.onRemove(c); }) ], ), @@ -106,7 +200,7 @@ class CargoTable extends StatelessWidget { padding: const EdgeInsets.only(right: 48.0), child: Align( alignment: Alignment.centerRight, - child: Text(total.toString(), + child: Text(total.toStringAsFixed(2), style: TextStyle(fontWeight: FontWeight.bold))), ), ), diff --git a/lib/pages/carton/carton_editor.dart b/lib/pages/carton/carton_editor.dart index a2746dc..878ad41 100644 --- a/lib/pages/carton/carton_editor.dart +++ b/lib/pages/carton/carton_editor.dart @@ -1,27 +1,23 @@ import 'package:fcs/domain/constants.dart'; import 'package:fcs/domain/entities/carton.dart'; import 'package:fcs/domain/entities/cargo_type.dart'; +import 'package:fcs/domain/entities/carton_size.dart'; import 'package:fcs/domain/entities/fcs_shipment.dart'; import 'package:fcs/domain/entities/package.dart'; import 'package:fcs/domain/entities/user.dart'; import 'package:fcs/domain/vo/delivery_address.dart'; import 'package:fcs/helpers/theme.dart'; -import 'package:fcs/localization/app_translations.dart'; import 'package:fcs/pages/carton/carton_cargo_table.dart'; -import 'package:fcs/pages/carton/carton_mix_table.dart'; import 'package:fcs/pages/carton/carton_package_table.dart'; +import 'package:fcs/pages/carton_size/carton_size_list.dart'; +import 'package:fcs/pages/delivery_address/delivery_address_list.dart'; import 'package:fcs/pages/delivery_address/delivery_address_row.dart'; -import 'package:fcs/pages/delivery_address/model/delivery_address_model.dart'; import 'package:fcs/pages/fcs_shipment/model/fcs_shipment_model.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/package/model/package_model.dart'; import 'package:fcs/pages/rates/model/shipment_rate_model.dart'; import 'package:fcs/pages/user_search/user_serach.dart'; -import 'package:fcs/pages/widgets/bottom_up_page_route.dart'; import 'package:fcs/pages/widgets/defalut_delivery_address.dart'; -import 'package:fcs/pages/widgets/delivery_address_selection.dart'; import 'package:fcs/pages/widgets/display_text.dart'; import 'package:fcs/pages/widgets/fcs_id_icon.dart'; import 'package:fcs/pages/widgets/length_picker.dart'; @@ -30,14 +26,15 @@ import 'package:fcs/pages/widgets/local_dropdown.dart'; import 'package:fcs/pages/widgets/local_radio_buttons.dart'; import 'package:fcs/pages/widgets/local_text.dart'; import 'package:fcs/pages/widgets/local_title.dart'; -import 'package:fcs/pages/widgets/my_data_table.dart'; import 'package:fcs/pages/widgets/progress.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_icons/flutter_icons.dart'; import 'package:provider/provider.dart'; +import 'cargo_type_addtion.dart'; import 'cargo_type_editor.dart'; import 'model/carton_model.dart'; +import '../carton_size/model/carton_size_model.dart'; import 'widgets.dart'; class CartonEditor extends StatefulWidget { @@ -93,9 +90,9 @@ class _CartonEditorState extends State { cargoTypes: [], packages: [], ); - _lengthController.text = "12"; - _widthController.text = "12"; - _heightController.text = "12"; + _lengthController.text = ""; + _widthController.text = ""; + _heightController.text = ""; _isNew = true; _selectedCartonType = carton_from_packages; _loadFcsShipments(); @@ -203,53 +200,60 @@ class _CartonEditorState extends State { labelTextKey: "box.mix.carton", iconData: MaterialCommunityIcons.package, ); - var fcsShipmentsBox = LocalDropdown( - callback: (v) { - setState(() { - _fcsShipment = v; - }); - _loadMixCartons(); - }, - labelKey: "shipment.pack.fcs.shipment", - iconData: Ionicons.ios_airplane, - display: (u) => u.shipmentNumber, - selectedValue: _fcsShipment, - values: _fcsShipments, - ); + var fcsShipmentsBox = Container( + padding: EdgeInsets.only(top: 10), + child: LocalDropdown( + callback: (v) { + setState(() { + _fcsShipment = v; + }); + _loadMixCartons(); + }, + labelKey: "shipment.pack.fcs.shipment", + iconData: Ionicons.ios_airplane, + display: (u) => u.shipmentNumber, + selectedValue: _fcsShipment, + values: _fcsShipments, + )); - var mixCartonsBox = LocalDropdown( - callback: (v) { - setState(() { - _mixCarton = v; - }); - }, - labelKey: "box.mix.carton", - iconData: MaterialCommunityIcons.package, - display: (u) => u.cartonNumber, - selectedValue: _mixCarton, - values: _mixCartons, - ); + var mixCartonsBox = Container( + padding: EdgeInsets.only(top: 10), + child: LocalDropdown( + callback: (v) { + setState(() { + _mixCarton = v; + }); + }, + labelKey: "box.mix.carton", + iconData: MaterialCommunityIcons.package, + display: (u) => u.cartonNumber, + selectedValue: _mixCarton, + values: _mixCartons, + )); - final fcsIDBox = Row( - children: [ - Expanded( - child: DisplayText( - text: _user?.fcsID ?? "", - labelTextKey: "box.fcs.id", - icon: FcsIDIcon(), - )), - _isNew - ? IconButton( - icon: Icon(Icons.search, color: primaryColor), - onPressed: () => searchUser(context, callbackUserSelect: (u) { - setState(() { - this._user = u; - _loadPackages(); - }); - })) - : Container(), - ], - ); + final fcsIDBox = Container( + padding: EdgeInsets.only(top: 10), + child: Row( + children: [ + Expanded( + child: DisplayText( + text: _user?.fcsID ?? "", + labelTextKey: "box.fcs.id", + icon: FcsIDIcon(), + )), + _isNew + ? IconButton( + icon: Icon(Icons.search, color: primaryColor), + onPressed: () => + searchUser(context, callbackUserSelect: (u) { + setState(() { + this._user = u; + _loadPackages(); + }); + })) + : Container(), + ], + )); final namebox = DisplayText( text: _user?.name ?? "", @@ -260,14 +264,17 @@ class _CartonEditorState extends State { final lengthBox = LengthPicker( controller: _lengthController, lableKey: "box.length", + isReadOnly: true, ); final widthBox = LengthPicker( controller: _widthController, lableKey: "box.width", + isReadOnly: true, ); final heightBox = LengthPicker( controller: _heightController, lableKey: "box.height", + isReadOnly: true, ); final dimBox = Row( @@ -284,7 +291,7 @@ class _CartonEditorState extends State { ); final shipmentWeightBox = DisplayText( - text: shipmentWeight != null ? shipmentWeight.toStringAsFixed(0) : "", + text: shipmentWeight != null ? shipmentWeight.toStringAsFixed(2) : "", labelTextKey: "box.shipment_weight", iconData: MaterialCommunityIcons.weight, ); @@ -317,9 +324,17 @@ class _CartonEditorState extends State { color: primaryColor, ), onPressed: () async { - CargoType cargo = await Navigator.push(context, - CupertinoPageRoute(builder: (context) => CargoTypeEditor())); - _addCargo(cargo); + // CargoType cargo = await Navigator.push(context, + // CupertinoPageRoute(builder: (context) => CargoTypeEditor())); + // _addCargo(cargo); + List cargos = await Navigator.push>( + context, + CupertinoPageRoute(builder: (context) => CargoTypeAddition())); + if (cargos == null) return; + setState(() { + _carton.cargoTypes.clear(); + _carton.cargoTypes.addAll(cargos); + }); }), ); @@ -367,7 +382,9 @@ class _CartonEditorState extends State { LocalTitle(textKey: "box.shipment_info"), _isNew ? fcsShipmentsBox : shipmentBox, isSmallBag - ? _isNew ? mixCartonsBox : mixCartonNumberBox + ? _isNew + ? mixCartonsBox + : mixCartonNumberBox : Container(), ...(isMixBox ? [ @@ -402,12 +419,25 @@ class _CartonEditorState extends State { isSmallBag ? Container() : LocalTitle(textKey: "box.dimension"), + isSmallBag ? Container() : cartonSizeDropdown(), isSmallBag ? Container() : dimBox, isSmallBag ? Container() : shipmentWeightBox, LocalTitle(textKey: "box.delivery_address"), DefaultDeliveryAddress( deliveryAddress: _deliveryAddress, labelKey: "box.delivery_address", + onTap: () async { + DeliveryAddress _address = await Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => DeliveryAddressList( + isAdminCreation: true, + deliveryAddress: _deliveryAddress))); + if (_address == null) return; + setState(() { + _deliveryAddress = _address; + }); + }, ), ]), _isNew ? createBtn : saveBtn, @@ -421,6 +451,85 @@ class _CartonEditorState extends State { ); } + CartonSize selectedCatonSize; + Widget cartonSizeDropdown() { + List _cartonSizes = + Provider.of(context).getCartonSizes; + + return Padding( + padding: const EdgeInsets.only(top: 10), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 0, right: 10), + child: Icon(AntDesign.CodeSandbox, color: primaryColor), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(right: 18.0), + child: LocalText( + context, + "box.carton_size", + color: Colors.black54, + fontSize: 16, + ), + ), + DropdownButton( + isDense: true, + value: selectedCatonSize, + style: TextStyle(color: Colors.black, fontSize: 14), + underline: Container( + height: 1, + color: Colors.grey, + ), + onChanged: (CartonSize newValue) { + setState(() { + if (newValue.name == MANAGE_CARTONSIZE) { + selectedCatonSize = null; + _manageCartonSize(); + return; + } + selectedCatonSize = newValue; + _widthController.text = + selectedCatonSize.width.toString(); + _heightController.text = + selectedCatonSize.height.toString(); + _lengthController.text = + selectedCatonSize.length.toString(); + }); + }, + isExpanded: true, + items: _cartonSizes + .map>((CartonSize value) { + return DropdownMenuItem( + value: value, + child: Text(value.name ?? "", + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: value.name == MANAGE_CARTONSIZE + ? secondaryColor + : primaryColor)), + ); + }).toList(), + ), + ], + ), + ), + ], + ), + ); + } + + _manageCartonSize() { + Navigator.push( + context, + CupertinoPageRoute(builder: (context) => CartonSizeList()), + ); + } + List getAddressList( BuildContext context, List addresses) { return addresses.asMap().entries.map((s) { diff --git a/lib/pages/carton/carton_info.dart b/lib/pages/carton/carton_info.dart index 37daf5c..0cea278 100644 --- a/lib/pages/carton/carton_info.dart +++ b/lib/pages/carton/carton_info.dart @@ -177,7 +177,7 @@ class _CartonInfoState extends State { ); final shipmentWeightBox = DisplayText( - text: shipmentWeight != null ? shipmentWeight.toStringAsFixed(0) : "", + text: shipmentWeight != null ? shipmentWeight.toStringAsFixed(2) : "", labelTextKey: "box.shipment_weight", iconData: MaterialCommunityIcons.weight, ); diff --git a/lib/pages/carton/carton_list_row.dart b/lib/pages/carton/carton_list_row.dart index 54c4a7e..7e5cf27 100644 --- a/lib/pages/carton/carton_list_row.dart +++ b/lib/pages/carton/carton_list_row.dart @@ -78,7 +78,7 @@ class CartonListRow extends StatelessWidget { child: Row( children: [ new Text( - "${box.actualWeight?.toString() ?? ''} lb", + "${box.actualWeight?.toStringAsFixed(2) ?? ''} lb", style: new TextStyle(fontSize: 15.0, color: Colors.grey), ), diff --git a/lib/pages/carton/total_weight_edit.dart b/lib/pages/carton/total_weight_edit.dart new file mode 100644 index 0000000..614e0d5 --- /dev/null +++ b/lib/pages/carton/total_weight_edit.dart @@ -0,0 +1,86 @@ +import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/widgets/input_text.dart'; +import 'package:fcs/pages/widgets/local_text.dart'; +import 'package:fcs/pages/widgets/progress.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:fcs/pages/main/util.dart'; + +typedef void ProfileCallback(); + +class TotalWeightEdit extends StatefulWidget { + final double totalWeight; + + const TotalWeightEdit({Key key, this.totalWeight}) : super(key: key); + @override + _TotalWeightEditState createState() => _TotalWeightEditState(); +} + +class _TotalWeightEditState extends State { + final TextEditingController totalController = new TextEditingController(); + bool _loading = false; + + @override + void initState() { + super.initState(); + totalController.text = widget.totalWeight.toStringAsFixed(2); + } + + @override + Widget build(BuildContext context) { + final totalInputBox = InputText( + labelTextKey: 'shipment.cargo.total', + iconData: FontAwesomeIcons.weightHanging, + textInputType: TextInputType.number, + controller: totalController); + + final saveBtn = + fcsButton(context, getLocalString(context, "btn.save"), callack: _save); + + return LocalProgress( + inAsyncCall: _loading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + title: LocalText( + context, + "box.cargo_total_title", + fontSize: 20, + color: primaryColor, + ), + backgroundColor: Colors.white, + shadowColor: Colors.transparent, + leading: IconButton( + icon: Icon( + CupertinoIcons.back, + size: 35, + color: primaryColor, + ), + onPressed: () => Navigator.of(context).pop(), + ), + ), + body: ListView( + padding: EdgeInsets.all(18), + children: [totalInputBox, SizedBox(height: 30), saveBtn], + ), + ), + ); + } + + _save() async { + setState(() { + _loading = true; + }); + try { + double total = double.parse(totalController.text, (s) => 0); + Navigator.pop(context, total); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _loading = false; + }); + } + } +} diff --git a/lib/pages/carton_size/carton_size_editor.dart b/lib/pages/carton_size/carton_size_editor.dart new file mode 100644 index 0000000..6c4d51d --- /dev/null +++ b/lib/pages/carton_size/carton_size_editor.dart @@ -0,0 +1,168 @@ +import 'package:fcs/domain/entities/cargo_type.dart'; +import 'package:fcs/domain/entities/carton_size.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/input_text.dart'; +import 'package:fcs/pages/widgets/length_picker.dart'; +import 'package:fcs/pages/widgets/local_dropdown.dart'; +import 'package:fcs/pages/widgets/local_text.dart'; +import 'package:fcs/pages/widgets/progress.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_icons/flutter_icons.dart'; +import 'package:provider/provider.dart'; + +import 'model/carton_size_model.dart'; + +class CartonSizeEditor extends StatefulWidget { + final CartonSize cartonSize; + CartonSizeEditor({this.cartonSize}); + + @override + _CartonSizeEditorState createState() => _CartonSizeEditorState(); +} + +class _CartonSizeEditorState extends State { + TextEditingController _nameController = new TextEditingController(); + TextEditingController _widthController = new TextEditingController(); + TextEditingController _heightController = new TextEditingController(); + TextEditingController _lengthController = new TextEditingController(); + + bool _isLoading = false; + CartonSize _cartonSize; + bool _isNew; + + @override + void initState() { + super.initState(); + if (widget.cartonSize != null) { + _cartonSize = widget.cartonSize; + _isNew = false; + _nameController.text = _cartonSize.name; + _widthController.text = _cartonSize.width.toString(); + _heightController.text = _cartonSize.height.toString(); + _lengthController.text = _cartonSize.length.toString(); + } else { + _lengthController.text = "12"; + _widthController.text = "12"; + _heightController.text = "12"; + _isNew = true; + } + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final nameBox = InputText( + labelTextKey: 'box.carton_size.name', + iconData: Icons.text_format, + controller: _nameController); + + final lengthBox = LengthPicker( + controller: _lengthController, + lableKey: "box.length", + ); + final widthBox = LengthPicker( + controller: _widthController, + lableKey: "box.width", + ); + final heightBox = LengthPicker( + controller: _heightController, + lableKey: "box.height", + ); + + final dimBox = Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Icon(FontAwesome.arrow_circle_right, color: primaryColor), + ), + SizedBox(child: lengthBox, width: 80), + SizedBox(child: widthBox, width: 80), + SizedBox(child: heightBox, width: 80), + ], + ); + + final saveBtn = fcsButton( + context, + getLocalString(context, 'box.cargo.save.btn'), + callack: () async { + if (_nameController.text == "") { + showMsgDialog(context, "Esrror", "Invalid carton size name!"); + return; + } + setState(() { + _isLoading = true; + }); + CartonSizeModel cartonSizeModel = + Provider.of(context, listen: false); + try { + double l = double.parse(_lengthController.text, (s) => 0); + double w = double.parse(_widthController.text, (s) => 0); + double h = double.parse(_heightController.text, (s) => 0); + + if (_isNew) { + CartonSize _cartonSize = CartonSize( + name: _nameController.text, length: l, width: w, height: h); + await cartonSizeModel.addCartonSize(_cartonSize); + } else { + CartonSize _cartonSize = CartonSize( + id: widget.cartonSize.id, + name: _nameController.text, + length: l, + width: w, + height: h); + await cartonSizeModel.updateCartonSize(_cartonSize); + } + + Navigator.pop(context); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + }, + ); + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: + new Icon(CupertinoIcons.back, color: primaryColor, size: 30), + onPressed: () => Navigator.of(context).pop(), + ), + shadowColor: Colors.transparent, + backgroundColor: Colors.white, + title: LocalText( + context, + "box.carton_size", + fontSize: 20, + color: primaryColor, + )), + body: Container( + padding: EdgeInsets.all(18), + child: ListView( + shrinkWrap: true, + children: [ + nameBox, + dimBox, + SizedBox(height: 40), + saveBtn, + SizedBox(height: 20), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/carton_size/carton_size_list.dart b/lib/pages/carton_size/carton_size_list.dart new file mode 100644 index 0000000..98a2687 --- /dev/null +++ b/lib/pages/carton_size/carton_size_list.dart @@ -0,0 +1,147 @@ +import 'package:fcs/domain/entities/carton_size.dart'; +import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/main/util.dart'; +import 'package:fcs/pages/widgets/local_text.dart'; +import 'package:fcs/pages/widgets/progress.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'carton_size_editor.dart'; +import 'model/carton_size_model.dart'; + +typedef void FindCallBack(); + +class CartonSizeList extends StatefulWidget { + const CartonSizeList(); + @override + _CartonSizeListState createState() => _CartonSizeListState(); +} + +class _CartonSizeListState extends State { + bool _isLoading = false; + + @override + void initState() { + super.initState(); + } + + List showCartonSize( + BuildContext context, List cartonSizes) { + return cartonSizes.map((p) { + return new ListTile( + onTap: () { + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => CartonSizeEditor(cartonSize: p))); + }, + title: new Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Text( + p.name, + style: TextStyle(fontSize: 15.0), + ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: new Text( + "(L${p.length}xW${p.width}xH${p.height})", + style: new TextStyle(color: Colors.grey), + ), + ), + SizedBox(height: 15) + ], + ), + ), + Spacer(), + IconButton( + icon: Icon(Icons.remove, color: primaryColor), + onPressed: () => _remove(p), + ) + ], + )); + }).toList(); + } + + @override + Widget build(BuildContext context) { + List cartonSizes = + Provider.of(context).cartonSizes; + + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: + new Icon(CupertinoIcons.back, color: primaryColor, size: 30), + onPressed: () => Navigator.of(context).pop(), + ), + shadowColor: Colors.transparent, + backgroundColor: Colors.white, + title: LocalText( + context, + "box.carton_size.title", + fontSize: 20, + color: primaryColor, + ), + ), + body: Padding( + padding: const EdgeInsets.only(left: 12.0, right: 12), + child: ListView( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.only(right: 18.0), + child: IconButton( + icon: Icon(Icons.add, color: primaryColor), + onPressed: () { + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => CartonSizeEditor())); + }, + ), + ), + ], + ), + Column( + children: showCartonSize(context, cartonSizes), + ), + SizedBox( + height: 10, + ) + ], + ), + ), + )); + } + + _remove(CartonSize cartonSize) { + if (cartonSize == null) { + showMsgDialog(context, "Esrror", "Invalid cartonsize!"); + return; + } + showConfirmDialog(context, "box.carton_size_remove.confirm", + () => _removeCartonSize(cartonSize)); + } + + _removeCartonSize(CartonSize cartonSize) async { + setState(() { + _isLoading = true; + }); + CartonSizeModel cartonSizeModel = + Provider.of(context, listen: false); + try { + await cartonSizeModel.deleteCartonSize(cartonSize.id); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } +} diff --git a/lib/pages/carton_size/model/carton_size_model.dart b/lib/pages/carton_size/model/carton_size_model.dart new file mode 100644 index 0000000..a42d0e3 --- /dev/null +++ b/lib/pages/carton_size/model/carton_size_model.dart @@ -0,0 +1,45 @@ +import 'dart:async'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:fcs/domain/entities/carton_size.dart'; +import 'package:fcs/pages/main/model/base_model.dart'; +import 'package:logging/logging.dart'; + +const MANAGE_CARTONSIZE = "Manage Carton Size"; + +class CartonSizeModel extends BaseModel { + List cartonSizes = []; + final log = Logger('CartonSizeModel'); + + StreamSubscription listener; + + List get getCartonSizes { + var _cartonSizes = new List.from(cartonSizes); + return _cartonSizes..insert(0, CartonSize(name: MANAGE_CARTONSIZE)); + } + + void initUser(user) { + super.initUser(user); + cartonSizes = [ + CartonSize( + id: "1", name: "FCS Small Box", length: 16, width: 12, height: 12), + CartonSize( + id: "2", name: "FCS Medium Box", length: 22, width: 16, height: 15), + CartonSize( + id: "3", name: "FCS Large Box", length: 28, width: 15, height: 16) + ]; + } + + @override + logout() async { + if (listener != null) await listener.cancel(); + + cartonSizes = []; + } + + Future addCartonSize(CartonSize cartonSize) async {} + + Future updateCartonSize(CartonSize cartonSize) async {} + + Future deleteCartonSize(String id) async {} +} diff --git a/lib/pages/delivery_address/delivery_address_list.dart b/lib/pages/delivery_address/delivery_address_list.dart index b096bef..c8455ba 100644 --- a/lib/pages/delivery_address/delivery_address_list.dart +++ b/lib/pages/delivery_address/delivery_address_list.dart @@ -12,7 +12,11 @@ import 'delivery_address_row.dart'; import 'model/delivery_address_model.dart'; class DeliveryAddressList extends StatefulWidget { - const DeliveryAddressList({Key key}) : super(key: key); + final bool isAdminCreation; + final DeliveryAddress deliveryAddress; + const DeliveryAddressList( + {Key key, this.isAdminCreation = false, this.deliveryAddress}) + : super(key: key); @override _DeliveryAddressListState createState() => _DeliveryAddressListState(); } @@ -80,8 +84,13 @@ class _DeliveryAddressListState extends State { child: Padding( padding: const EdgeInsets.all(10.0), child: Icon(Icons.check, - color: - deliveryAddress.isDefault ? primaryColor : Colors.black26), + color: widget.isAdminCreation + ? widget.deliveryAddress.id == deliveryAddress.id + ? primaryColor + : Colors.black26 + : deliveryAddress.isDefault + ? primaryColor + : Colors.black26), ), ), Expanded( @@ -104,18 +113,24 @@ class _DeliveryAddressListState extends State { } Future _select(DeliveryAddress deliveryAddress) async { - if (deliveryAddress.isDefault) { - Navigator.pop(context); - return; - } setState(() { _isLoading = true; }); - var deliveryAddressModel = - Provider.of(context, listen: false); + try { - await deliveryAddressModel.selectDefalutDeliveryAddress(deliveryAddress); - Navigator.pop(context); + if (widget.isAdminCreation) { + Navigator.pop(context, deliveryAddress); + } else { + if (deliveryAddress.isDefault) { + Navigator.pop(context); + return; + } + var deliveryAddressModel = + Provider.of(context, listen: false); + await deliveryAddressModel + .selectDefalutDeliveryAddress(deliveryAddress); + Navigator.pop(context); + } } catch (e) { showMsgDialog(context, "Error", e.toString()); } finally { diff --git a/lib/pages/fcs_shipment/fcs_shipment_editor.dart b/lib/pages/fcs_shipment/fcs_shipment_editor.dart index 11bffd3..43643af 100644 --- a/lib/pages/fcs_shipment/fcs_shipment_editor.dart +++ b/lib/pages/fcs_shipment/fcs_shipment_editor.dart @@ -59,6 +59,9 @@ class _FcsShipmentEditorState extends State { _consigneeController.text = _shipment.consignee; _portController.text = _shipment.port; _destinationController.text = _shipment.destination; + }else{ + var mainModel = Provider.of(context,listen: false); + _currentShipmentType =mainModel.setting.shipmentTypes[0]; } } @@ -113,11 +116,11 @@ class _FcsShipmentEditorState extends State { iconData: Icons.date_range, controller: _cutoffDateController, ), - InputDate( - labelTextKey: "FCSshipment.departure_date", - iconData: Icons.date_range, - controller: _departureDateControler, - ), + // InputDate( + // labelTextKey: "FCSshipment.departure_date", + // iconData: Icons.date_range, + // controller: _departureDateControler, + // ), InputDate( labelTextKey: "FCSshipment.ETA", iconData: Icons.date_range, @@ -184,13 +187,13 @@ class _FcsShipmentEditorState extends State { try { var cutoffDate = _cutoffDateController.text; var arrivalDate = _arrivalDateController.text; - var depDate = _departureDateControler.text; + // var depDate = _departureDateControler.text; fcsShipment.cutoffDate = cutoffDate == "" ? null : dateFormatter.parse(cutoffDate); fcsShipment.arrivalDate = arrivalDate == "" ? null : dateFormatter.parse(arrivalDate); - fcsShipment.departureDate = - depDate == "" ? null : dateFormatter.parse(depDate); + // fcsShipment.departureDate = + // depDate == "" ? null : dateFormatter.parse(depDate); } catch (e) { showMsgDialog(context, "Error", e.toString()); // shold never happen } @@ -214,10 +217,10 @@ class _FcsShipmentEditorState extends State { await showMsgDialog(context, "Error", "Invalid ETA date!"); return false; } - if (fcsShipment.departureDate == null) { - await showMsgDialog(context, "Error", "Invalid departure date!"); - return false; - } + // if (fcsShipment.departureDate == null) { + // await showMsgDialog(context, "Error", "Invalid departure date!"); + // return false; + // } return true; } diff --git a/lib/pages/fcs_shipment/fcs_shipment_info.dart b/lib/pages/fcs_shipment/fcs_shipment_info.dart index 819e7f6..091fe30 100644 --- a/lib/pages/fcs_shipment/fcs_shipment_info.dart +++ b/lib/pages/fcs_shipment/fcs_shipment_info.dart @@ -159,7 +159,7 @@ class _FcsShipmentInfoState extends State { child: ListView(children: [ shipmentNumberBox, cutoffDateDBox, - departureDateBox, + // departureDateBox, etaBox, shipTypeBox, consigneeBox, diff --git a/lib/pages/main/home_page.dart b/lib/pages/main/home_page.dart index 237a20f..48080c8 100644 --- a/lib/pages/main/home_page.dart +++ b/lib/pages/main/home_page.dart @@ -24,6 +24,7 @@ import 'package:fcs/pages/package/model/package_model.dart'; import 'package:fcs/pages/package/package_info.dart'; import 'package:fcs/pages/package/package_list.dart'; import 'package:fcs/pages/processing/processing_list.dart'; +import 'package:fcs/pages/rates/shipment_rates.dart'; import 'package:fcs/pages/receiving/receiving_list.dart'; import 'package:fcs/pages/shipment/shipment_list.dart'; import 'package:fcs/pages/staff/staff_list.dart'; @@ -224,6 +225,12 @@ class _HomePageState extends State { icon: Octicons.package, btnCallback: () => Navigator.of(context).push( CupertinoPageRoute(builder: (context) => PackageList()))); + + final rateBtnFcs = TaskButton("rate.title", + icon: Ionicons.ios_calculator, + btnCallback: () => Navigator.of(context).push( + CupertinoPageRoute(builder: (context) => ShipmentRates()))); + final packagesBtnFcs = TaskButton("package.btn.name", icon: Octicons.package, btnCallback: () => Navigator.of(context).push(CupertinoPageRoute( @@ -323,6 +330,7 @@ class _HomePageState extends State { widgets.add(shipmentBtn); widgets.add(invoicesBtn); + if (user.hasAdmin() || user.hasSupport()) widgetsFcs.add(rateBtnFcs); if (user.hasPackages()) widgetsFcs.add(packagesBtnFcs); if (user.hasShipment()) widgetsFcs.add(shipmentBtnFcs); if (user.hasInvoices()) widgetsFcs.add(invoicesBtnFcs); diff --git a/lib/pages/package/model/package_model.dart b/lib/pages/package/model/package_model.dart index f3f5470..7ee7672 100644 --- a/lib/pages/package/model/package_model.dart +++ b/lib/pages/package/model/package_model.dart @@ -156,6 +156,7 @@ class PackageModel extends BaseModel { var snaps = await Firestore.instance .collection("$path") .where("tracking_id", isEqualTo: trackingID) + .where("is_deleted", isEqualTo: false) .getDocuments(source: Source.server); if (snaps.documents.length == 1) { var snap = snaps.documents[0]; diff --git a/lib/pages/processing/model/processing_model.dart b/lib/pages/processing/model/processing_model.dart new file mode 100644 index 0000000..e5782c8 --- /dev/null +++ b/lib/pages/processing/model/processing_model.dart @@ -0,0 +1,30 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:fcs/domain/entities/package.dart'; +import 'package:fcs/domain/entities/processing.dart'; +import 'package:fcs/pages/main/model/base_model.dart'; +import 'package:logging/logging.dart'; + +class ProcessingModel extends BaseModel { + List processings = []; + final log = Logger('ProcessingModel'); + + StreamSubscription listener; + + void initUser(user) { + super.initUser(user); + } + + @override + logout() async { + if (listener != null) await listener.cancel(); + + processings = []; + } + + Future createProcessing(Processing processing) {} + + Future updateProcessing(Processing processing) {} +} diff --git a/lib/pages/processing/package_editor.dart b/lib/pages/processing/package_editor.dart new file mode 100644 index 0000000..e997eca --- /dev/null +++ b/lib/pages/processing/package_editor.dart @@ -0,0 +1,282 @@ +import 'package:fcs/domain/entities/market.dart'; +import 'package:fcs/domain/entities/package.dart'; +import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/market/market_editor.dart'; +import 'package:fcs/pages/market/model/market_model.dart'; +import 'package:fcs/pages/package/tracking_id_page.dart'; +import 'package:fcs/pages/main/util.dart'; +import 'package:fcs/pages/widgets/barcode_scanner.dart'; +import 'package:fcs/pages/widgets/input_text.dart'; +import 'package:fcs/pages/widgets/local_text.dart'; +import 'package:fcs/pages/widgets/multi_img_controller.dart'; +import 'package:fcs/pages/widgets/multi_img_file.dart'; +import 'package:fcs/pages/widgets/progress.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_icons/flutter_icons.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:provider/provider.dart'; + +class PackageEditor extends StatefulWidget { + final Package package; + PackageEditor({this.package}); + + @override + _PackageEditorState createState() => _PackageEditorState(); +} + +class _PackageEditorState extends State { + TextEditingController _remarkCtl = new TextEditingController(); + TextEditingController _descCtl = new TextEditingController(); + TextEditingController _trackingIDCtl = new TextEditingController(); + + bool _isLoading = false; + bool _isNew; + MultiImgController multiImgController = MultiImgController(); + Package _package = Package(); + + @override + void initState() { + super.initState(); + if (widget.package != null) { + _isNew = false; + _package = widget.package; + _trackingIDCtl.text = _package.trackingID; + + selectedMarket = _package.market ?? ""; + _descCtl.text = _package.desc; + _remarkCtl.text = _package.remark; + multiImgController.setImageFiles = _package.photoFiles; + } else { + _isNew = true; + } + } + + @override + Widget build(BuildContext context) { + final trackingIDBox = Container( + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: InputText( + iconData: MaterialCommunityIcons.barcode_scan, + labelTextKey: "processing.tracking.id", + controller: _trackingIDCtl, + )), + InkWell( + onTap: _scan, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Icon( + MaterialCommunityIcons.barcode_scan, + color: primaryColor, + ), + Text("Scan") + ], + ), + ), + ), + ], + )); + + final descBox = InputText( + labelTextKey: 'processing.desc', + iconData: MaterialCommunityIcons.message_text_outline, + controller: _descCtl); + + final remarkBox = InputText( + labelTextKey: 'processing.remark', + iconData: Entypo.new_message, + controller: _remarkCtl); + + final img = MultiImageFile( + enabled: true, + controller: multiImgController, + title: "Receipt File", + ); + + final selectBtn = fcsButton( + context, + getLocalString(context, 'processing.package.select.btn'), + callack: _selectPackage, + ); + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: new Icon(CupertinoIcons.back, color: primaryColor, size: 30), + onPressed: () => Navigator.of(context).pop(), + ), + shadowColor: Colors.transparent, + backgroundColor: Colors.white, + title: widget.package == null + ? LocalText( + context, + "processing.package.create", + fontSize: 20, + color: primaryColor, + ) + : LocalText( + context, + "processing.package.update", + fontSize: 20, + color: primaryColor, + ), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: ListView( + children: [ + trackingIDBox, + SizedBox( + height: 10, + ), + img, + Divider(), + SizedBox( + height: 15, + ), + marketDropdown(), + descBox, + remarkBox, + SizedBox( + height: 20, + ), + selectBtn, + SizedBox( + height: 20, + ) + ], + ), + ), + ), + ); + } + + String selectedMarket; + Widget marketDropdown() { + List _markets = Provider.of(context).markets; + List markets = _markets.map((e) => e.name).toList(); + markets.insert(0, MANAGE_MARKET); + if (!markets.contains(selectedMarket)) { + markets.insert(0, selectedMarket); + } + + return Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 0, right: 10), + child: Icon(Icons.store, color: primaryColor), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(right: 18.0), + child: LocalText( + context, + "processing.market", + color: Colors.black54, + fontSize: 16, + ), + ), + DropdownButton( + isDense: true, + value: selectedMarket, + style: TextStyle(color: Colors.black, fontSize: 14), + underline: Container( + height: 1, + color: Colors.grey, + ), + onChanged: (String newValue) { + setState(() { + if (newValue == MANAGE_MARKET) { + selectedMarket = null; + _manageMarket(); + return; + } + selectedMarket = newValue; + }); + }, + isExpanded: true, + items: markets.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value ?? "", + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: value == MANAGE_MARKET + ? secondaryColor + : primaryColor)), + ); + }).toList(), + ), + ], + ), + ), + ], + ); + } + + _manageMarket() { + Navigator.push( + context, + CupertinoPageRoute(builder: (context) => MarketEditor()), + ); + } + + _scan() async { + PermissionStatus permission = + await PermissionHandler().checkPermissionStatus(PermissionGroup.camera); + if (permission != PermissionStatus.granted) { + Map permissions = + await PermissionHandler() + .requestPermissions([PermissionGroup.camera]); + if (permissions[PermissionGroup.camera] != PermissionStatus.granted) { + showMsgDialog(context, "Error", "Camera permission is not granted"); + return null; + } + } + + try { + String barcode = await scanBarcode(); + if (barcode != null) { + setState(() { + _trackingIDCtl.text = barcode; + }); + } + } catch (e) { + print('error: $e'); + } + } + + _selectPackage() async { + setState(() { + _isLoading = true; + }); + + try { + this._package.trackingID = _trackingIDCtl.text; + this._package.market = selectedMarket; + this._package.desc = _descCtl.text; + this._package.remark = _remarkCtl.text; + this._package.photoFiles = _isNew + ? multiImgController.getAddedFile + : multiImgController.getUpdatedFile; + + Navigator.pop(context, this._package); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } +} diff --git a/lib/pages/processing/processing_edit_editor.dart b/lib/pages/processing/processing_edit_editor.dart new file mode 100644 index 0000000..839d6a1 --- /dev/null +++ b/lib/pages/processing/processing_edit_editor.dart @@ -0,0 +1,260 @@ +import 'package:fcs/domain/entities/market.dart'; +import 'package:fcs/domain/entities/package.dart'; +import 'package:fcs/domain/entities/user.dart'; +import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/market/market_editor.dart'; +import 'package:fcs/pages/market/model/market_model.dart'; +import 'package:fcs/pages/package/model/package_model.dart'; +import 'package:fcs/pages/package/tracking_id_page.dart'; +import 'package:fcs/pages/main/util.dart'; +import 'package:fcs/pages/user_search/user_serach.dart'; +import 'package:fcs/pages/widgets/display_text.dart'; +import 'package:fcs/pages/widgets/fcs_id_icon.dart'; +import 'package:fcs/pages/widgets/input_text.dart'; +import 'package:fcs/pages/widgets/local_text.dart'; +import 'package:fcs/pages/widgets/multi_img_controller.dart'; +import 'package:fcs/pages/widgets/multi_img_file.dart'; +import 'package:fcs/pages/widgets/progress.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_icons/flutter_icons.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; + +class ProcessingEditEditor extends StatefulWidget { + final Package package; + ProcessingEditEditor({this.package}); + + @override + _ProcessingEditEditorState createState() => _ProcessingEditEditorState(); +} + +class _ProcessingEditEditorState extends State { + TextEditingController _remarkCtl = new TextEditingController(); + TextEditingController _descCtl = new TextEditingController(); + + Package _package; + User _user; + bool _isLoading = false; + + @override + void initState() { + super.initState(); + _package = widget.package; + selectedMarket = _package.market ?? ""; + _descCtl.text = _package.desc; + _remarkCtl.text = _package.remark; + multiImgController.setImageUrls = _package.photoUrls; + _user = User( + fcsID: _package.fcsID ?? "", + name: _package.userName ?? "", + phoneNumber: _package.phoneNumber ?? ""); + } + + final DateFormat dateFormat = DateFormat("d MMM yyyy"); + + bool isNew = false; + MultiImgController multiImgController = MultiImgController(); + + @override + Widget build(BuildContext context) { + var fcsIDBox = Row( + children: [ + Expanded( + child: DisplayText( + text: _user.fcsID, + labelTextKey: "processing.fcs.id", + icon: FcsIDIcon(), + )), + IconButton( + icon: Icon(Icons.search, color: primaryColor), + onPressed: () => searchUser(context, callbackUserSelect: (u) { + setState(() { + this._user = u; + }); + })), + ], + ); + final namebox = DisplayText( + text: _user.name, + labelTextKey: "processing.name", + iconData: Icons.person, + ); + final phoneNumberBox = DisplayText( + text: _user.phoneNumber, + labelTextKey: "processing.phone", + iconData: Icons.phone, + ); + + final trackingIdBox = DisplayText( + text: _package.trackingID, + labelTextKey: "processing.tracking.id", + iconData: MaterialCommunityIcons.barcode_scan, + ); + final completeProcessingBtn = fcsButton( + context, + getLocalString(context, 'processing.edit.complete.btn'), + callack: _completeProcessing, + ); + final descBox = InputText( + labelTextKey: 'processing.desc', + iconData: MaterialCommunityIcons.message_text_outline, + controller: _descCtl); + final remarkBox = InputText( + labelTextKey: 'processing.remark', + iconData: Entypo.new_message, + controller: _remarkCtl); + + final img = MultiImageFile( + enabled: true, + controller: multiImgController, + title: "Receipt File", + ); + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: new Icon(CupertinoIcons.back, color: primaryColor, size: 30), + onPressed: () => Navigator.of(context).pop(), + ), + shadowColor: Colors.transparent, + backgroundColor: Colors.white, + title: LocalText( + context, + "processing.edit.title", + fontSize: 20, + color: primaryColor, + ), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: ListView( + children: [ + trackingIdBox, + fcsIDBox, + namebox, + phoneNumberBox, + marketDropdown(), + descBox, + remarkBox, + img, + completeProcessingBtn, + SizedBox( + height: 20, + ) + ], + ), + ), + ), + ); + } + + String selectedMarket; + Widget marketDropdown() { + List _markets = Provider.of(context).markets; + List markets = _markets.map((e) => e.name).toList(); + markets.insert(0, MANAGE_MARKET); + if (!markets.contains(selectedMarket)) { + markets.insert(0, selectedMarket); + } + + return Padding( + padding: const EdgeInsets.only(left: 5.0, right: 0), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 0, right: 10), + child: Icon(Icons.store, color: primaryColor), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(right: 18.0), + child: LocalText( + context, + "processing.market", + color: Colors.black54, + fontSize: 16, + ), + ), + DropdownButton( + isDense: true, + value: selectedMarket, + style: TextStyle(color: Colors.black, fontSize: 14), + underline: Container( + height: 1, + color: Colors.grey, + ), + onChanged: (String newValue) { + setState(() { + if (newValue == MANAGE_MARKET) { + selectedMarket = null; + _manageMarket(); + return; + } + selectedMarket = newValue; + }); + }, + isExpanded: true, + items: markets.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value ?? "", + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: value == MANAGE_MARKET + ? secondaryColor + : primaryColor)), + ); + }).toList(), + ), + ], + ), + ), + ], + ), + ); + } + + _manageMarket() { + Navigator.push( + context, + CupertinoPageRoute(builder: (context) => MarketEditor()), + ); + } + + _completeProcessing() async { + if (_descCtl.text == "") { + showMsgDialog(context, "Error", "Expected some description"); + return; + } + if (_user.fcsID == null || _user.fcsID == "") { + showMsgDialog(context, "Error", "Expected FCS-ID"); + return; + } + setState(() { + _isLoading = true; + }); + PackageModel packageModel = + Provider.of(context, listen: false); + try { + _package.fcsID = _user.fcsID; + _package.desc = _descCtl.text; + _package.remark = _remarkCtl.text; + _package.market = selectedMarket; + await packageModel.updateProcessing(_package, + multiImgController.getAddedFile, multiImgController.getDeletedUrl); + Navigator.pop(context); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } +} diff --git a/lib/pages/processing/processing_editor.dart b/lib/pages/processing/processing_editor.dart index e521dc3..6021019 100644 --- a/lib/pages/processing/processing_editor.dart +++ b/lib/pages/processing/processing_editor.dart @@ -1,69 +1,61 @@ -import 'package:fcs/domain/entities/market.dart'; import 'package:fcs/domain/entities/package.dart'; +import 'package:fcs/domain/entities/processing.dart'; import 'package:fcs/domain/entities/user.dart'; import 'package:fcs/helpers/theme.dart'; -import 'package:fcs/pages/market/market_editor.dart'; -import 'package:fcs/pages/market/model/market_model.dart'; -import 'package:fcs/pages/package/model/package_model.dart'; -import 'package:fcs/pages/package/tracking_id_page.dart'; import 'package:fcs/pages/main/util.dart'; import 'package:fcs/pages/user_search/user_serach.dart'; -import 'package:fcs/pages/widgets/bottom_up_page_route.dart'; import 'package:fcs/pages/widgets/display_text.dart'; import 'package:fcs/pages/widgets/fcs_id_icon.dart'; -import 'package:fcs/pages/widgets/input_text.dart'; import 'package:fcs/pages/widgets/local_text.dart'; -import 'package:fcs/pages/widgets/multi_img_controller.dart'; -import 'package:fcs/pages/widgets/multi_img_file.dart'; import 'package:fcs/pages/widgets/progress.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_icons/flutter_icons.dart'; -import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; -class ProcessingEditor extends StatefulWidget { - final Package package; - ProcessingEditor({this.package}); +import 'model/processing_model.dart'; +import 'package_editor.dart'; +class ProcesingEditor extends StatefulWidget { + final Processing processing; + const ProcesingEditor({this.processing}); @override - _ProcessingEditorState createState() => _ProcessingEditorState(); + _ProcesingEditorState createState() => _ProcesingEditorState(); } -class _ProcessingEditorState extends State { - TextEditingController _remarkCtl = new TextEditingController(); - TextEditingController _descCtl = new TextEditingController(); - - Package _package; - User _user; +class _ProcesingEditorState extends State { + Processing processing = Processing(); bool _isLoading = false; + bool _isNew; + User user; + User shipper; + List packages = []; @override void initState() { super.initState(); - _package = widget.package; - selectedMarket = _package.market ?? ""; - _descCtl.text = _package.desc; - _remarkCtl.text = _package.remark; - multiImgController.setImageUrls = _package.photoUrls; - _user = User( - fcsID: _package.fcsID ?? "", - name: _package.userName ?? "", - phoneNumber: _package.phoneNumber ?? ""); + _isNew = widget.processing == null; + if (!_isNew) { + processing = widget.processing; + user = User( + fcsID: processing.userID, + name: processing.userName, + phoneNumber: processing.userPhoneNumber); + shipper = User( + fcsID: processing.fcsID, + name: processing.shipperName, + phoneNumber: processing.shipperPhoneNumber); + packages = processing.packages; + } } - final DateFormat dateFormat = DateFormat("d MMM yyyy"); - - bool isNew = false; - MultiImgController multiImgController = MultiImgController(); - @override Widget build(BuildContext context) { var fcsIDBox = Row( children: [ Expanded( child: DisplayText( - text: _user.fcsID, + text: user != null ? user.fcsID : "", labelTextKey: "processing.fcs.id", icon: FcsIDIcon(), )), @@ -71,184 +63,254 @@ class _ProcessingEditorState extends State { icon: Icon(Icons.search, color: primaryColor), onPressed: () => searchUser(context, callbackUserSelect: (u) { setState(() { - this._user = u; + this.user = u; }); })), ], ); - final namebox = DisplayText( - text: _user.name, - labelTextKey: "processing.name", - iconData: Icons.person, - ); + final phoneNumberBox = DisplayText( - text: _user.phoneNumber, + text: user != null ? user.phoneNumber : "", labelTextKey: "processing.phone", + maxLines: 2, iconData: Icons.phone, ); - final trackingIdBox = DisplayText( - text: _package.trackingID, - labelTextKey: "processing.tracking.id", - iconData: MaterialCommunityIcons.barcode_scan, + final namebox = DisplayText( + text: user != null ? user.name : "", + labelTextKey: "processing.consignee.name", + maxLines: 2, + iconData: Icons.person, ); - final completeProcessingBtn = fcsButton( - context, - getLocalString(context, 'processing.edit.complete.btn'), - callack: _completeProcessing, - ); - final descBox = InputText( - labelTextKey: 'processing.desc', - iconData: MaterialCommunityIcons.message_text_outline, - controller: _descCtl); - final remarkBox = InputText( - labelTextKey: 'processing.remark', - iconData: Entypo.new_message, - controller: _remarkCtl); - final img = MultiImageFile( - enabled: true, - controller: multiImgController, - title: "Receipt File", - ); - return LocalProgress( - inAsyncCall: _isLoading, - child: Scaffold( - appBar: AppBar( - centerTitle: true, - leading: new IconButton( - icon: new Icon(CupertinoIcons.back, color: primaryColor, size: 30), - onPressed: () => Navigator.of(context).pop(), - ), - shadowColor: Colors.transparent, - backgroundColor: Colors.white, - title: LocalText( - context, - "processing.edit.title", - fontSize: 20, - color: primaryColor, - ), - ), - body: Padding( - padding: const EdgeInsets.all(8.0), - child: ListView( - children: [ - trackingIdBox, - fcsIDBox, - namebox, - phoneNumberBox, - marketDropdown(), - descBox, - remarkBox, - img, - completeProcessingBtn, - SizedBox( - height: 20, - ) - ], - ), - ), + final consigneeBox = Container( + child: Column( + children: [ + fcsIDBox, + phoneNumberBox, + namebox, + ], ), ); - } - String selectedMarket; - Widget marketDropdown() { - List _markets = Provider.of(context).markets; - List markets = _markets.map((e) => e.name).toList(); - markets.insert(0, MANAGE_MARKET); - if (!markets.contains(selectedMarket)) { - markets.insert(0, selectedMarket); - } + var shipperIDBox = Row( + children: [ + Expanded( + child: DisplayText( + text: shipper != null ? shipper.fcsID : "", + labelTextKey: "processing.fcs.id", + icon: FcsIDIcon(), + )), + IconButton( + icon: Icon(Icons.search, color: primaryColor), + onPressed: () => searchUser(context, callbackUserSelect: (u) { + setState(() { + this.shipper = u; + }); + })), + ], + ); - return Padding( - padding: const EdgeInsets.only(left: 5.0, right: 0), - child: Row( + final shipperPhoneNumberBox = DisplayText( + text: shipper != null ? shipper.phoneNumber : "", + labelTextKey: "processing.phone", + maxLines: 2, + iconData: Icons.phone, + ); + + final shipperNamebox = DisplayText( + text: shipper != null ? shipper.name : "", + labelTextKey: "processing.shipper.name", + maxLines: 2, + iconData: Icons.person, + ); + + final shipperBox = Container( + child: Column( children: [ - Padding( - padding: const EdgeInsets.only(left: 0, right: 10), - child: Icon(Icons.store, color: primaryColor), + shipperIDBox, + shipperPhoneNumberBox, + shipperNamebox, + ], + ), + ); + + final packageTitleBox = Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text("Packages (${packages.length})"), + IconButton( + icon: Icon( + Icons.add_circle, + color: primaryColor, + ), + onPressed: () async { + Package _package = await Navigator.push( + context, + CupertinoPageRoute(builder: (context) => PackageEditor()), + ); + _addPackage(_package); + // _savePackage(_package); + }), + ], + ), + ); + + final createButton = fcsButton( + context, + getLocalString(context, 'processing.edit.complete.btn'), + callack: _save, + ); + + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: + new Icon(CupertinoIcons.back, color: primaryColor, size: 30), + onPressed: () => Navigator.of(context).pop(), + ), + shadowColor: Colors.transparent, + backgroundColor: Colors.white, + title: LocalText( + context, + _isNew ? "processing.create" : "processing.update", + fontSize: 20, + color: primaryColor, + ), ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(right: 18.0), - child: LocalText( - context, - "processing.market", - color: Colors.black54, - fontSize: 16, - ), + body: Padding( + padding: const EdgeInsets.only(left: 12.0, right: 12), + child: ListView( + children: [ + SizedBox( + height: 10, ), - DropdownButton( - isDense: true, - value: selectedMarket, - style: TextStyle(color: Colors.black, fontSize: 14), - underline: Container( - height: 1, - color: Colors.grey, - ), - onChanged: (String newValue) { - setState(() { - if (newValue == MANAGE_MARKET) { - selectedMarket = null; - _manageMarket(); - return; - } - selectedMarket = newValue; - }); - }, - isExpanded: true, - items: markets.map>((String value) { - return DropdownMenuItem( - value: value, - child: Text(value ?? "", - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: value == MANAGE_MARKET - ? secondaryColor - : primaryColor)), - ); - }).toList(), + Row( + children: [ + Flexible(child: consigneeBox), + Flexible(child: shipperBox) + ], + ), + Divider(), + packageTitleBox, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _getPackages(context, packages), + ), + SizedBox( + height: 20, + ), + createButton, + SizedBox( + height: 10, ), ], ), ), - ], - ), - ); + )); } - _manageMarket() { - Navigator.push( - context, - CupertinoPageRoute(builder: (context) => MarketEditor()), - ); + List _getPackages(BuildContext context, List packages) { + return packages.map((p) { + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide(color: Colors.grey[300]), + ), + ), + child: InkWell( + onTap: () async { + Package _package = await Navigator.of(context).push( + CupertinoPageRoute( + builder: (context) => PackageEditor(package: p))); + // setState(() { + // _savePackage(_package); + // }); + _savePackage(_package); + }, + child: Row( + children: [ + Expanded( + child: new Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: new Row( + children: [ + new Padding( + padding: new EdgeInsets.symmetric( + horizontal: 30.0 - 15.0 / 2), + child: Stack( + alignment: AlignmentDirectional.bottomEnd, + children: [ + Icon( + Octicons.package, + color: primaryColor, + size: 30, + ), + ], + ), + ), + new Expanded( + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Text( + p.trackingID, + style: new TextStyle(fontSize: 15.0), + ), + ], + ), + ), + IconButton( + icon: Icon(Icons.remove, color: primaryColor), + onPressed: () => _removePackage(p), + ) + ], + ), + ), + ), + ], + ), + ), + ); + }).toList(); } - _completeProcessing() async { - if (_descCtl.text == "") { - showMsgDialog(context, "Error", "Expected some description"); - return; - } - if (_user.fcsID == null || _user.fcsID == "") { - showMsgDialog(context, "Error", "Expected FCS-ID"); - return; - } + _addPackage(Package package) { + if (package == null) return; + + this.packages.add(package); + setState(() {}); + } + + _savePackage(Package package) { + if (package == null) return; + setState(() {}); + } + + _removePackage(Package package) { + if (package == null) return; + this.packages.removeWhere((p) => p.trackingID == package.trackingID); + setState(() {}); + } + + _save() async { setState(() { _isLoading = true; }); - PackageModel packageModel = - Provider.of(context, listen: false); + + ProcessingModel processingModel = + Provider.of(context, listen: false); try { - _package.fcsID = _user.fcsID; - _package.desc = _descCtl.text; - _package.remark = _remarkCtl.text; - _package.market = selectedMarket; - await packageModel.updateProcessing(_package, - multiImgController.getAddedFile, multiImgController.getDeletedUrl); + if (_isNew) { + await processingModel.createProcessing(processing); + } else { + processing.id = widget.processing.id; + await processingModel.updateProcessing(processing); + } Navigator.pop(context); } catch (e) { showMsgDialog(context, "Error", e.toString()); diff --git a/lib/pages/processing/processing_info.dart b/lib/pages/processing/processing_info.dart index 0b6fe43..55aab36 100644 --- a/lib/pages/processing/processing_info.dart +++ b/lib/pages/processing/processing_info.dart @@ -16,7 +16,7 @@ import 'package:flutter_icons/flutter_icons.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; -import 'processing_editor.dart'; +import 'processing_edit_editor.dart'; final DateFormat dateFormat = DateFormat("d MMM yyyy"); @@ -179,7 +179,7 @@ class _ProcessingInfoState extends State { bool deleted = await Navigator.push( context, CupertinoPageRoute( - builder: (context) => ProcessingEditor( + builder: (context) => ProcessingEditEditor( package: widget.package, ))); if (deleted ?? false) { diff --git a/lib/pages/processing/processing_list.dart b/lib/pages/processing/processing_list.dart index 6ab3497..7f5855a 100644 --- a/lib/pages/processing/processing_list.dart +++ b/lib/pages/processing/processing_list.dart @@ -1,6 +1,5 @@ import 'package:fcs/domain/entities/package.dart'; import 'package:fcs/helpers/theme.dart'; -import 'package:fcs/pages/main/model/main_model.dart'; import 'package:fcs/pages/package/model/package_model.dart'; import 'package:fcs/pages/package_search/package_serach.dart'; import 'package:fcs/pages/widgets/local_text.dart'; @@ -10,6 +9,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'processing_editor.dart'; import 'processing_info.dart'; import 'processing_list_row.dart'; @@ -64,6 +64,14 @@ class _ProcessingListState extends State { ), ], ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () { + _newProcessing(); + }, + icon: Icon(Icons.add), + label: LocalText(context, "processing.new", color: Colors.white), + backgroundColor: primaryColor, + ), body: PaginatorListView( paginatorListener: packages, rowBuilder: (p) => ProcessingListRow( @@ -85,4 +93,11 @@ class _ProcessingListState extends State { builder: (context) => ProcessingInfo(package: _package)), ); } + + _newProcessing() { + Navigator.push( + context, + CupertinoPageRoute(builder: (context) => ProcesingEditor()), + ); + } } diff --git a/lib/pages/processing_old/processing_editor.dart b/lib/pages/processing_old/processing_editor.dart new file mode 100644 index 0000000..e521dc3 --- /dev/null +++ b/lib/pages/processing_old/processing_editor.dart @@ -0,0 +1,261 @@ +import 'package:fcs/domain/entities/market.dart'; +import 'package:fcs/domain/entities/package.dart'; +import 'package:fcs/domain/entities/user.dart'; +import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/market/market_editor.dart'; +import 'package:fcs/pages/market/model/market_model.dart'; +import 'package:fcs/pages/package/model/package_model.dart'; +import 'package:fcs/pages/package/tracking_id_page.dart'; +import 'package:fcs/pages/main/util.dart'; +import 'package:fcs/pages/user_search/user_serach.dart'; +import 'package:fcs/pages/widgets/bottom_up_page_route.dart'; +import 'package:fcs/pages/widgets/display_text.dart'; +import 'package:fcs/pages/widgets/fcs_id_icon.dart'; +import 'package:fcs/pages/widgets/input_text.dart'; +import 'package:fcs/pages/widgets/local_text.dart'; +import 'package:fcs/pages/widgets/multi_img_controller.dart'; +import 'package:fcs/pages/widgets/multi_img_file.dart'; +import 'package:fcs/pages/widgets/progress.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_icons/flutter_icons.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; + +class ProcessingEditor extends StatefulWidget { + final Package package; + ProcessingEditor({this.package}); + + @override + _ProcessingEditorState createState() => _ProcessingEditorState(); +} + +class _ProcessingEditorState extends State { + TextEditingController _remarkCtl = new TextEditingController(); + TextEditingController _descCtl = new TextEditingController(); + + Package _package; + User _user; + bool _isLoading = false; + + @override + void initState() { + super.initState(); + _package = widget.package; + selectedMarket = _package.market ?? ""; + _descCtl.text = _package.desc; + _remarkCtl.text = _package.remark; + multiImgController.setImageUrls = _package.photoUrls; + _user = User( + fcsID: _package.fcsID ?? "", + name: _package.userName ?? "", + phoneNumber: _package.phoneNumber ?? ""); + } + + final DateFormat dateFormat = DateFormat("d MMM yyyy"); + + bool isNew = false; + MultiImgController multiImgController = MultiImgController(); + + @override + Widget build(BuildContext context) { + var fcsIDBox = Row( + children: [ + Expanded( + child: DisplayText( + text: _user.fcsID, + labelTextKey: "processing.fcs.id", + icon: FcsIDIcon(), + )), + IconButton( + icon: Icon(Icons.search, color: primaryColor), + onPressed: () => searchUser(context, callbackUserSelect: (u) { + setState(() { + this._user = u; + }); + })), + ], + ); + final namebox = DisplayText( + text: _user.name, + labelTextKey: "processing.name", + iconData: Icons.person, + ); + final phoneNumberBox = DisplayText( + text: _user.phoneNumber, + labelTextKey: "processing.phone", + iconData: Icons.phone, + ); + + final trackingIdBox = DisplayText( + text: _package.trackingID, + labelTextKey: "processing.tracking.id", + iconData: MaterialCommunityIcons.barcode_scan, + ); + final completeProcessingBtn = fcsButton( + context, + getLocalString(context, 'processing.edit.complete.btn'), + callack: _completeProcessing, + ); + final descBox = InputText( + labelTextKey: 'processing.desc', + iconData: MaterialCommunityIcons.message_text_outline, + controller: _descCtl); + final remarkBox = InputText( + labelTextKey: 'processing.remark', + iconData: Entypo.new_message, + controller: _remarkCtl); + + final img = MultiImageFile( + enabled: true, + controller: multiImgController, + title: "Receipt File", + ); + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: new Icon(CupertinoIcons.back, color: primaryColor, size: 30), + onPressed: () => Navigator.of(context).pop(), + ), + shadowColor: Colors.transparent, + backgroundColor: Colors.white, + title: LocalText( + context, + "processing.edit.title", + fontSize: 20, + color: primaryColor, + ), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: ListView( + children: [ + trackingIdBox, + fcsIDBox, + namebox, + phoneNumberBox, + marketDropdown(), + descBox, + remarkBox, + img, + completeProcessingBtn, + SizedBox( + height: 20, + ) + ], + ), + ), + ), + ); + } + + String selectedMarket; + Widget marketDropdown() { + List _markets = Provider.of(context).markets; + List markets = _markets.map((e) => e.name).toList(); + markets.insert(0, MANAGE_MARKET); + if (!markets.contains(selectedMarket)) { + markets.insert(0, selectedMarket); + } + + return Padding( + padding: const EdgeInsets.only(left: 5.0, right: 0), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 0, right: 10), + child: Icon(Icons.store, color: primaryColor), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(right: 18.0), + child: LocalText( + context, + "processing.market", + color: Colors.black54, + fontSize: 16, + ), + ), + DropdownButton( + isDense: true, + value: selectedMarket, + style: TextStyle(color: Colors.black, fontSize: 14), + underline: Container( + height: 1, + color: Colors.grey, + ), + onChanged: (String newValue) { + setState(() { + if (newValue == MANAGE_MARKET) { + selectedMarket = null; + _manageMarket(); + return; + } + selectedMarket = newValue; + }); + }, + isExpanded: true, + items: markets.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value ?? "", + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: value == MANAGE_MARKET + ? secondaryColor + : primaryColor)), + ); + }).toList(), + ), + ], + ), + ), + ], + ), + ); + } + + _manageMarket() { + Navigator.push( + context, + CupertinoPageRoute(builder: (context) => MarketEditor()), + ); + } + + _completeProcessing() async { + if (_descCtl.text == "") { + showMsgDialog(context, "Error", "Expected some description"); + return; + } + if (_user.fcsID == null || _user.fcsID == "") { + showMsgDialog(context, "Error", "Expected FCS-ID"); + return; + } + setState(() { + _isLoading = true; + }); + PackageModel packageModel = + Provider.of(context, listen: false); + try { + _package.fcsID = _user.fcsID; + _package.desc = _descCtl.text; + _package.remark = _remarkCtl.text; + _package.market = selectedMarket; + await packageModel.updateProcessing(_package, + multiImgController.getAddedFile, multiImgController.getDeletedUrl); + Navigator.pop(context); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } +} diff --git a/lib/pages/processing_old/processing_info.dart b/lib/pages/processing_old/processing_info.dart new file mode 100644 index 0000000..0b6fe43 --- /dev/null +++ b/lib/pages/processing_old/processing_info.dart @@ -0,0 +1,194 @@ +import 'package:fcs/domain/entities/package.dart'; +import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/main/util.dart'; +import 'package:fcs/pages/package/model/package_model.dart'; +import 'package:fcs/pages/widgets/bottom_up_page_route.dart'; +import 'package:fcs/pages/widgets/display_text.dart'; +import 'package:fcs/pages/widgets/fcs_id_icon.dart'; +import 'package:fcs/pages/widgets/local_text.dart'; +import 'package:fcs/pages/widgets/multi_img_controller.dart'; +import 'package:fcs/pages/widgets/multi_img_file.dart'; +import 'package:fcs/pages/widgets/progress.dart'; +import 'package:fcs/pages/widgets/status_tree.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_icons/flutter_icons.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; + +import 'processing_editor.dart'; + +final DateFormat dateFormat = DateFormat("d MMM yyyy"); + +class ProcessingInfo extends StatefulWidget { + final Package package; + ProcessingInfo({this.package}); + + @override + _ProcessingInfoState createState() => _ProcessingInfoState(); +} + +class _ProcessingInfoState extends State { + var dateFormatter = new DateFormat('dd MMM yyyy'); + Package _package; + bool _isLoading = false; + MultiImgController multiImgController = MultiImgController(); + + @override + void initState() { + super.initState(); + initPackage(widget.package); + } + + initPackage(Package package) { + setState(() { + _package = package; + multiImgController.setImageUrls = package.photoUrls; + }); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final trackingIdBox = DisplayText( + text: _package.trackingID, + labelTextKey: "processing.tracking.id", + iconData: MaterialCommunityIcons.barcode_scan, + ); + var fcsIDBox = DisplayText( + text: _package.fcsID, + labelTextKey: "processing.fcs.id", + icon: FcsIDIcon(), + ); + final phoneNumberBox = DisplayText( + text: _package.phoneNumber, + labelTextKey: "processing.phone", + iconData: Icons.phone, + ); + final customerNameBox = DisplayText( + text: _package.userName, + labelTextKey: "processing.name", + iconData: Icons.perm_identity, + ); + final marketBox = DisplayText( + text: _package.market ?? "-", + labelTextKey: "processing.market", + iconData: Icons.store, + ); + final descBox = DisplayText( + text: _package.desc ?? "-", + labelTextKey: "processing.desc", + iconData: MaterialCommunityIcons.message_text_outline, + ); + final remarkBox = DisplayText( + text: _package.remark ?? "-", + labelTextKey: "processing.remark", + iconData: Entypo.new_message, + ); + final img = MultiImageFile( + enabled: false, + controller: multiImgController, + title: "Receipt File", + ); + + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: new Icon(CupertinoIcons.back, color: primaryColor, size: 30), + onPressed: () => Navigator.of(context).pop(), + ), + shadowColor: Colors.transparent, + backgroundColor: Colors.white, + title: LocalText( + context, + "processing.info.title", + fontSize: 20, + color: primaryColor, + ), + actions: [ + IconButton( + icon: Icon(Icons.delete, color: primaryColor), + onPressed: _delete, + ), + IconButton( + icon: Icon(Icons.edit, color: primaryColor), + onPressed: _gotoEditor, + ), + ], + ), + body: Card( + child: Column( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(10.0), + child: ListView(children: [ + trackingIdBox, + fcsIDBox, + customerNameBox, + phoneNumberBox, + marketBox, + descBox, + remarkBox, + _package.photoUrls.length == 0 ? Container() : img, + StatusTree( + shipmentHistory: _package.shipmentHistory, + currentStatus: _package.status), + SizedBox( + height: 20, + ) + ]), + )), + ], + ), + ), + ), + ); + } + + _delete() { + showConfirmDialog(context, "processing.delete.confirm", _deletePackage); + } + + _deletePackage() async { + setState(() { + _isLoading = true; + }); + PackageModel packageModel = + Provider.of(context, listen: false); + try { + await packageModel.deleteProcessing(_package); + Navigator.pop(context, true); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } + + _gotoEditor() async { + bool deleted = await Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ProcessingEditor( + package: widget.package, + ))); + if (deleted ?? false) { + Navigator.pop(context); + } else { + PackageModel packageModel = + Provider.of(context, listen: false); + Package p = await packageModel.getPackage(_package.id); + initPackage(p); + } + } +} diff --git a/lib/pages/processing_old/processing_list.dart b/lib/pages/processing_old/processing_list.dart new file mode 100644 index 0000000..6ab3497 --- /dev/null +++ b/lib/pages/processing_old/processing_list.dart @@ -0,0 +1,88 @@ +import 'package:fcs/domain/entities/package.dart'; +import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/main/model/main_model.dart'; +import 'package:fcs/pages/package/model/package_model.dart'; +import 'package:fcs/pages/package_search/package_serach.dart'; +import 'package:fcs/pages/widgets/local_text.dart'; +import 'package:fcs/pages/widgets/progress.dart'; +import 'package:fcs/pagination/paginator_listview.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import 'processing_info.dart'; +import 'processing_list_row.dart'; + +class ProcessingList extends StatefulWidget { + @override + _ProcessingListState createState() => _ProcessingListState(); +} + +class _ProcessingListState extends State { + bool _isLoading = false; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + var packageModel = Provider.of(context); + var packages = packageModel.activePackages; + + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: new Icon(CupertinoIcons.back), + onPressed: () => Navigator.of(context).pop(), + ), + backgroundColor: primaryColor, + title: LocalText( + context, + "processing.title", + fontSize: 20, + color: Colors.white, + ), + actions: [ + IconButton( + icon: Icon( + Icons.search, + color: Colors.white, + ), + iconSize: 30, + onPressed: () => searchPackage(context, + callbackPackageSelect: _searchCallback), + ), + ], + ), + body: PaginatorListView( + paginatorListener: packages, + rowBuilder: (p) => ProcessingListRow( + key: ValueKey(p.id), + package: p, + ), + color: primaryColor, + )), + ); + } + + _searchCallback(Package package) async { + var packageModel = Provider.of(context, listen: false); + Package _package = await packageModel.getPackage(package.id); + if (_package == null) return; + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ProcessingInfo(package: _package)), + ); + } +} diff --git a/lib/pages/processing_old/processing_list_row.dart b/lib/pages/processing_old/processing_list_row.dart new file mode 100644 index 0000000..cc06c16 --- /dev/null +++ b/lib/pages/processing_old/processing_list_row.dart @@ -0,0 +1,100 @@ +import 'package:fcs/domain/entities/package.dart'; +import 'package:fcs/helpers/theme.dart'; +import 'package:fcs/pages/main/util.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_icons/flutter_icons.dart'; +import 'package:intl/intl.dart'; + +import 'processing_info.dart'; + +typedef CallbackPackageSelect(Package package); + +class ProcessingListRow extends StatelessWidget { + final Package package; + final CallbackPackageSelect callbackPackageSelect; + final double dotSize = 15.0; + final DateFormat dateFormat = new DateFormat("dd MMM yyyy"); + + ProcessingListRow({Key key, this.package, this.callbackPackageSelect}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () { + if (callbackPackageSelect != null) { + callbackPackageSelect(package); + return; + } + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ProcessingInfo(package: package)), + ); + }, + child: Container( + padding: EdgeInsets.only(left: 15, right: 15), + child: Row( + children: [ + Expanded( + child: new Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: new Row( + children: [ + Container( + padding: EdgeInsets.only(left: 5, right: 10), + child: Icon( + FontAwesome.dropbox, + color: primaryColor, + size: 30, + ), + ), + new Expanded( + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: new Text( + package.id == null ? '' : package.trackingID, + style: new TextStyle( + fontSize: 15.0, color: Colors.black), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: new Text( + package.market == null ? '' : package.market, + style: new TextStyle( + fontSize: 15.0, color: Colors.black), + ), + ), + ], + ), + ), + ], + ), + ), + ), + Column( + children: [ + Padding( + padding: const EdgeInsets.all(3.0), + child: getStatus(package.status), + ), + Padding( + padding: const EdgeInsets.all(0), + child: new Text( + dateFormat.format(package.currentStatusDate), + style: new TextStyle(fontSize: 15.0, color: Colors.grey), + ), + ), + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/pages/rates/discount_by weight_list.dart b/lib/pages/rates/discount_by weight_list.dart index 84ee955..73b7467 100644 --- a/lib/pages/rates/discount_by weight_list.dart +++ b/lib/pages/rates/discount_by weight_list.dart @@ -82,7 +82,8 @@ class _DiscountByWeightListState extends State { discountByWeight: discountByWeight))); }, child: Container( - child: _row("${discountByWeight.weight.toString()} lb", + child: _row( + "${discountByWeight.weight.toStringAsFixed(2)} lb", "\$ " + discountByWeight.discount.toString()), ), ); diff --git a/lib/pages/rates/discount_by_weight_editor.dart b/lib/pages/rates/discount_by_weight_editor.dart index c271183..018c942 100644 --- a/lib/pages/rates/discount_by_weight_editor.dart +++ b/lib/pages/rates/discount_by_weight_editor.dart @@ -31,7 +31,7 @@ class _DiscountByWeightEditorState extends State { super.initState(); if (widget.discountByWeight != null) { _discountByWeight = widget.discountByWeight; - _weightController.text = _discountByWeight.weight.toString(); + _weightController.text = _discountByWeight.weight.toStringAsFixed(2); _discountController.text = _discountByWeight.discount.toString(); _isNew = false; } else { diff --git a/lib/pages/rates/shipment_rates.dart b/lib/pages/rates/shipment_rates.dart index f5f6a8a..aa351a6 100644 --- a/lib/pages/rates/shipment_rates.dart +++ b/lib/pages/rates/shipment_rates.dart @@ -196,8 +196,8 @@ class _ShipmentRatesState extends State { if (discounts == null) return []; return discounts.map((d) { return Container( - child: _row( - "${d.weight.toString()} lb", "\$ " + d.discount.toString(), ''), + child: _row("${d.weight.toStringAsFixed(2)} lb", + "\$ " + d.discount.toString(), ''), ); }).toList(); } diff --git a/lib/pages/rates/shipment_rates_edit.dart b/lib/pages/rates/shipment_rates_edit.dart index 286be1c..6fa144d 100644 --- a/lib/pages/rates/shipment_rates_edit.dart +++ b/lib/pages/rates/shipment_rates_edit.dart @@ -37,7 +37,7 @@ class _ShipmentRatesEditState extends State { Provider.of(context, listen: false); Rate rate = shipmentRateModel.rate; - _minWeight.text = rate.freeDeliveryWeight?.toString() ?? ""; + _minWeight.text = rate.freeDeliveryWeight?.toStringAsFixed(2) ?? ""; _deliveryFee.text = rate.deliveryFee?.toString() ?? ""; _volumetricRatio.text = rate.volumetricRatio?.toString() ?? ""; } diff --git a/lib/pages/shipment/box_row.dart b/lib/pages/shipment/box_row.dart index 3568be2..5b41300 100644 --- a/lib/pages/shipment/box_row.dart +++ b/lib/pages/shipment/box_row.dart @@ -101,7 +101,7 @@ class BoxRow extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 8.0), child: new Text( - "Actual Weight:${box.actualWeight.toString()}lb", + "Actual Weight:${box.actualWeight.toStringAsFixed(2)}lb", style: new TextStyle(fontSize: 14.0, color: Colors.grey), ), ), diff --git a/lib/pages/shipment/shipment_box_editor.dart b/lib/pages/shipment/shipment_box_editor.dart index 9b974f3..5d0b46d 100644 --- a/lib/pages/shipment/shipment_box_editor.dart +++ b/lib/pages/shipment/shipment_box_editor.dart @@ -232,7 +232,7 @@ class _ShipmentBoxEditorState extends State { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - Text(c.weight == null ? "0" : c.weight.toString(), + Text(c.weight == null ? "0" : c.weight.toStringAsFixed(2), style: textStyle), IconButton( icon: Icon( @@ -265,7 +265,7 @@ class _ShipmentBoxEditorState extends State { padding: const EdgeInsets.only(right: 48.0), child: Align( alignment: Alignment.centerRight, - child: Text(total.toString(), + child: Text(total.toStringAsFixed(2), style: TextStyle(fontWeight: FontWeight.bold))), ), ), diff --git a/lib/pages/widgets/length_picker.dart b/lib/pages/widgets/length_picker.dart index 32ae511..b384bef 100644 --- a/lib/pages/widgets/length_picker.dart +++ b/lib/pages/widgets/length_picker.dart @@ -9,9 +9,14 @@ class LengthPicker extends StatefulWidget { final TextEditingController controller; final String lableKey; final bool isReadOnly; + final bool displayFeet; const LengthPicker( - {Key key, this.controller, this.lableKey, this.isReadOnly = false}) + {Key key, + this.controller, + this.lableKey, + this.isReadOnly = false, + this.displayFeet = false}) : super(key: key); @override @@ -34,10 +39,12 @@ class _LengthPickerState extends State { _setText() { double v = double.parse(widget.controller.text, (s) => 0); + int _v = v.toInt(); + int f = (v / 12).floor(); int ins = (v % 12).round(); - _controller.text = "$f' $ins\""; + _controller.text = widget.displayFeet ? "$f' $ins\"" : "$_v\""; } @override @@ -51,7 +58,7 @@ class _LengthPickerState extends State { labelTextKey: widget.lableKey, controller: _controller, textInputType: TextInputType.number, - textAlign: TextAlign.end, + textAlign: widget.displayFeet ? TextAlign.end : TextAlign.center, )), ); } @@ -63,6 +70,7 @@ class _LengthPickerState extends State { return LengthPickerDialog( controller: widget.controller, labelKey: widget.lableKey, + displayFeet: widget.displayFeet, ); }, ); @@ -72,8 +80,10 @@ class _LengthPickerState extends State { class LengthPickerDialog extends StatefulWidget { final TextEditingController controller; final String labelKey; + final bool displayFeet; - const LengthPickerDialog({Key key, this.controller, this.labelKey}) + const LengthPickerDialog( + {Key key, this.controller, this.labelKey, this.displayFeet}) : super(key: key); @override @@ -91,7 +101,7 @@ class _LengthPickerDialogState extends State { if (widget.controller != null) { double v = double.parse(widget.controller.text, (s) => 0); _valueFeet = (v / 12).floor(); - _valueInc = (v % 12).toInt(); + _valueInc = widget.displayFeet ? (v % 12).toInt() : v.toInt(); } } @@ -107,31 +117,33 @@ class _LengthPickerDialogState extends State { )), children: [ Center(child: Text(_getText())), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(top: 8.0, left: 8), - child: LocalText( - context, - "feet", - color: primaryColor, - fontSize: 12, - ), - ), - Slider( - activeColor: primaryColor, - value: _valueFeet.toDouble(), - min: 0, - max: 15, - divisions: 100, - label: (_valueFeet ?? 0).round().toString(), - onChanged: (double v) { - _updateFeet(v); - }, - ), - ], - ), + widget.displayFeet + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(top: 8.0, left: 8), + child: LocalText( + context, + "feet", + color: primaryColor, + fontSize: 12, + ), + ), + Slider( + activeColor: primaryColor, + value: _valueFeet.toDouble(), + min: 0, + max: 15, + divisions: 100, + label: (_valueFeet ?? 0).round().toString(), + onChanged: (double v) { + _updateFeet(v); + }, + ), + ], + ) + : Container(), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -148,7 +160,7 @@ class _LengthPickerDialogState extends State { activeColor: primaryColor, value: _valueInc.toDouble(), min: 0, - max: 11, + max: widget.displayFeet ? 11 : 50, divisions: 100, label: (_valueInc ?? 0).round().toString(), onChanged: (double v) { @@ -177,13 +189,15 @@ class _LengthPickerDialogState extends State { }); if (widget.controller != null) { int _v = _valueInc.round() + _valueFeet.round() * 12; - widget.controller.text = _v.toString(); + widget.controller.text = + widget.displayFeet ? _v.toString() : _valueInc.toString(); } } String _getText() { int ft = _valueFeet.round(); int ins = _valueInc.round(); - return "$ft\' $ins\""; + + return widget.displayFeet ? "$ft\' $ins\"" : "$ins\""; } } diff --git a/lib/pages/widgets/multi_img_controller.dart b/lib/pages/widgets/multi_img_controller.dart index 1e10c7e..84654da 100644 --- a/lib/pages/widgets/multi_img_controller.dart +++ b/lib/pages/widgets/multi_img_controller.dart @@ -5,6 +5,7 @@ import 'display_image_source.dart'; class MultiImgController { List imageUrls = []; + List imageFiles = []; List addedFiles = []; List removedFiles = []; @@ -29,6 +30,22 @@ class MultiImgController { } } + set setImageFiles(List imageFiles) { + if (imageFiles == null) { + return; + } + + fileContainers.clear(); + + this.imageFiles = imageFiles; + imageFiles.forEach((e) { + fileContainers.add(DisplayImageSource(file: e)); + }); + if (callback != null) { + callback(); + } + } + void onChange(CallBack callBack) { this.callback = callBack; } @@ -51,11 +68,22 @@ class MultiImgController { if (imageUrls.contains(fileContainer.url)) { removedFiles.add(fileContainer); } + + if (imageFiles.contains(fileContainer.file)) { + this.imageFiles.remove(fileContainer.file); + } + if (callback != null) { callback(); } } + List get getUpdatedFile { + List _addfiles = getAddedFile; + this.imageFiles.addAll(_addfiles); + return this.imageFiles; + } + List get getAddedFile { return addedFiles.map((e) => e.file).toList(); } diff --git a/pubspec.lock b/pubspec.lock index cf0475c..7bcd555 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.2" + version: "2.5.0-nullsafety.1" barcode_scan: dependency: "direct main" description: @@ -21,7 +21,7 @@ packages: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety.1" cached_network_image: dependency: "direct main" description: @@ -42,21 +42,21 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.1.0-nullsafety.3" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" + version: "1.2.0-nullsafety.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.1.0-nullsafety.1" cloud_firestore: dependency: "direct main" description: @@ -84,14 +84,14 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.13" + version: "1.15.0-nullsafety.3" connectivity: dependency: "direct main" description: name: connectivity url: "https://pub.dartlang.org" source: hosted - version: "0.4.9+2" + version: "2.0.2" connectivity_for_web: dependency: transitive description: @@ -175,7 +175,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0-nullsafety.1" ffi: dependency: transitive description: @@ -398,14 +398,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.8" + version: "0.12.10-nullsafety.1" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.1.8" + version: "1.3.0-nullsafety.3" mime: dependency: transitive description: @@ -447,7 +447,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0-nullsafety.1" path_provider: dependency: "direct main" description: @@ -525,13 +525,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.1" - platform_detect: - dependency: transitive - description: - name: platform_detect - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.0" plugin_platform_interface: dependency: transitive description: @@ -560,13 +553,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.3.2+1" - pub_semver: - dependency: transitive - description: - name: pub_semver - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.4" quill_delta: dependency: transitive description: @@ -601,7 +587,7 @@ packages: name: share url: "https://pub.dartlang.org" source: hosted - version: "0.6.5" + version: "0.6.5+4" shared_preferences: dependency: "direct main" description: @@ -648,7 +634,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0-nullsafety.2" sqflite: dependency: transitive description: @@ -669,21 +655,21 @@ packages: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.5" + version: "1.10.0-nullsafety.1" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety.1" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.1.0-nullsafety.1" synchronized: dependency: transitive description: @@ -697,14 +683,14 @@ packages: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0-nullsafety.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.17" + version: "0.2.19-nullsafety.2" timeline_list: dependency: "direct main" description: @@ -718,14 +704,14 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0-nullsafety.3" url_launcher: dependency: "direct main" description: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "5.5.0" + version: "5.7.10" url_launcher_linux: dependency: transitive description: @@ -746,14 +732,21 @@ packages: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.7" + version: "1.0.9" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.2+1" + version: "0.1.5+1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+3" uuid: dependency: "direct main" description: @@ -767,7 +760,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.1.0-nullsafety.3" win32: dependency: transitive description: @@ -785,12 +778,10 @@ packages: zefyr: dependency: "direct main" description: - path: "packages/zefyr" - ref: HEAD - resolved-ref: "20b890b7714fd54ccd0028da5b354d3d8ac68251" - url: "git://github.com/masewo/zefyr.git" - source: git - version: "0.11.0" + name: zefyr + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.0" sdks: - dart: ">=2.9.0 <3.0.0" - flutter: ">=1.20.0 <2.0.0" + dart: ">=2.10.0-110 <2.11.0" + flutter: ">=1.22.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 3e93c9f..b530941 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,15 +35,11 @@ dependencies: photo_view: ^0.9.0 uuid: ^2.0.4 zefyr: - git: - url: git://github.com/masewo/zefyr.git - path: packages/zefyr - commit: 2e379982e97cc9b5aeb4fc0fcd85d6d32833f4ea path_provider: ^1.6.11 camera: ^0.4.2 - url_launcher: ^5.4.1 + url_launcher: ^5.7.10 device_info: ^0.4.1+4 - connectivity: ^0.4.8+1 + connectivity: ^2.0.2 logging: ^0.11.4 permission_handler: ^4.0.0 country_code_picker: ^1.3.12 @@ -53,8 +49,8 @@ dependencies: timeline_list: ^0.0.5 barcode_scan: ^2.0.2 flutter_pdfview: ^1.0.3 - flutter_local_notifications: ^1.4.4+4 - share: ^0.6.5 + flutter_local_notifications: ^1.4.4+4 + share: '>=0.6.5+4 <2.0.0' cached_network_image: ^2.3.2+1 flutter_cache_manager: ^2.0.0