Merge branch 'master' of tzw/fcs into master

This commit is contained in:
2024-02-06 07:36:01 +06:30
committed by Gogs
27 changed files with 2405 additions and 164 deletions

View File

@@ -266,7 +266,7 @@
"box.delivery_type":"Delivery type", "box.delivery_type":"Delivery type",
"box.fcs_shipment_num":"Shipment", "box.fcs_shipment_num":"Shipment",
"box.fcs.id":"FCS ID", "box.fcs.id":"FCS ID",
"box.name":"Customer name", "box.name":"Sender",
"box.phone":"Phone number", "box.phone":"Phone number",
"box.actual_weight":"Actual weight", "box.actual_weight":"Actual weight",
"box.add_cargo":"Add cargo", "box.add_cargo":"Add cargo",
@@ -326,6 +326,14 @@
"box.package_size":"Package", "box.package_size":"Package",
"box.select.cartion":"Select cartons", "box.select.cartion":"Select cartons",
"box.no_carton":"There is no cartons in this shipment.", "box.no_carton":"There is no cartons in this shipment.",
"box.crete.carton":"Create carton",
"box.carton.type":"Carton Type",
"box.select.delivery":"Select delivery type",
"box.select.package":"Select packages",
"box.no_package":"There is no packages.",
"box.input_cargo_weight":"Input cargo weight (lb)",
"box.input_surcharge_item":"Input surcharge items",
"box.select.cargo_type":"Select surcharge item",
"Boxes End ================================================================":"", "Boxes End ================================================================":"",
"Delivery Start ================================================================":"", "Delivery Start ================================================================":"",
@@ -592,7 +600,7 @@
"processing.new":"New Processing", "processing.new":"New Processing",
"processing.create":"New Processing", "processing.create":"New Processing",
"processing.update":"Update Processing", "processing.update":"Update Processing",
"processing.consignee.name":"Consignee name", "processing.consignee.name":"Consignee",
"processing.shipper.name":"Sender name", "processing.shipper.name":"Sender name",
"processing.package.select.btn":"Select", "processing.package.select.btn":"Select",
"processing.package.create":"New Package", "processing.package.create":"New Package",

View File

@@ -266,7 +266,7 @@
"box.delivery_type":"ပို့ဆောင်ရမည့်အမျိုးအစား", "box.delivery_type":"ပို့ဆောင်ရမည့်အမျိုးအစား",
"box.fcs_shipment_num":"တင်ပို့နံပါတ်", "box.fcs_shipment_num":"တင်ပို့နံပါတ်",
"box.fcs.id":"FCS ID", "box.fcs.id":"FCS ID",
"box.name":"နာမည်", "box.name":"ပေးပို့သူ",
"box.package":"အထုပ်များ", "box.package":"အထုပ်များ",
"box.phone":"ဖုန်းနံပါတ်", "box.phone":"ဖုန်းနံပါတ်",
"box.actual_weight":"အမှန်အလေးချိန်", "box.actual_weight":"အမှန်အလေးချိန်",
@@ -325,6 +325,14 @@
"box.package_size":"Package", "box.package_size":"Package",
"box.select.cartion":"Select cartons", "box.select.cartion":"Select cartons",
"box.no_carton":"There is no cartons in this shipment.", "box.no_carton":"There is no cartons in this shipment.",
"box.crete.carton":"Create carton",
"box.carton.type":"Carton Type",
"box.select.delivery":"Select delivery type",
"box.select.package":"Select packages",
"box.no_package":"There is no packages.",
"box.input_cargo_weight":"Input cargo weight (lb)",
"box.input_surcharge_item":"Input surcharge items",
"box.select.cargo_type":"Select surcharge item",
"Boxes End ================================================================":"", "Boxes End ================================================================":"",
"Delivery Start ================================================================":"", "Delivery Start ================================================================":"",
@@ -595,7 +603,7 @@
"processing.new":"လုပ်ဆောင်ခြင်း အသစ်", "processing.new":"လုပ်ဆောင်ခြင်း အသစ်",
"processing.create":"လုပ်ဆောင်ခြင်း အသစ်", "processing.create":"လုပ်ဆောင်ခြင်း အသစ်",
"processing.update":"လုပ်ဆောင်ခြင်း ပြင်ဆင်ခြင်း", "processing.update":"လုပ်ဆောင်ခြင်း ပြင်ဆင်ခြင်း",
"processing.consignee.name":"လက်ခံသူ အမည်", "processing.consignee.name":"လက်ခံသူ",
"processing.shipper.name":"တင်ပို့သူ အမည်", "processing.shipper.name":"တင်ပို့သူ အမည်",
"processing.package.select.btn":"ရွေးချယ်မည်", "processing.package.select.btn":"ရွေးချယ်မည်",
"processing.package.create":"အထုပ် အသစ်", "processing.package.create":"အထုပ် အသစ်",

View File

@@ -30,6 +30,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'pages/carton/model/carton_selection_model.dart'; import 'pages/carton/model/carton_selection_model.dart';
import 'pages/carton/model/package_selection_model.dart';
import 'pages/delivery/model/delivery_model.dart'; import 'pages/delivery/model/delivery_model.dart';
class App extends StatefulWidget { class App extends StatefulWidget {
@@ -64,6 +65,7 @@ class _AppState extends State<App> {
final ProcessingModel processingModel = new ProcessingModel(); final ProcessingModel processingModel = new ProcessingModel();
final PickupModel pickupModel = new PickupModel(); final PickupModel pickupModel = new PickupModel();
final CartonSelectionModel cartonSelectionModel = new CartonSelectionModel(); final CartonSelectionModel cartonSelectionModel = new CartonSelectionModel();
final PackageSelectionModel packageSelectionModel = new PackageSelectionModel();
late AppTranslationsDelegate _newLocaleDelegate; late AppTranslationsDelegate _newLocaleDelegate;
@@ -87,7 +89,8 @@ class _AppState extends State<App> {
..addModel(cartonSizeModel) ..addModel(cartonSizeModel)
..addModel(processingModel) ..addModel(processingModel)
..addModel(pickupModel) ..addModel(pickupModel)
..addModel(cartonSelectionModel); ..addModel(cartonSelectionModel)
..addModel(packageSelectionModel);
_newLocaleDelegate = AppTranslationsDelegate( _newLocaleDelegate = AppTranslationsDelegate(
newLocale: Translation().supportedLocales().first); newLocale: Translation().supportedLocales().first);
@@ -137,6 +140,7 @@ class _AppState extends State<App> {
ChangeNotifierProvider.value(value: processingModel), ChangeNotifierProvider.value(value: processingModel),
ChangeNotifierProvider.value(value: pickupModel), ChangeNotifierProvider.value(value: pickupModel),
ChangeNotifierProvider.value(value: cartonSelectionModel), ChangeNotifierProvider.value(value: cartonSelectionModel),
ChangeNotifierProvider.value(value: packageSelectionModel),
], ],
child: Consumer<LanguageModel>( child: Consumer<LanguageModel>(
builder: (context, value, child) { builder: (context, value, child) {

View File

@@ -133,3 +133,7 @@ const invoice_paid_status = "paid";
const payment_pending_status = "pending"; const payment_pending_status = "pending";
const payment_confirmed_status = "confirmed"; const payment_confirmed_status = "confirmed";
const payment_canceled_status = "canceled"; const payment_canceled_status = "canceled";
//Delivery types
const delivery_caton = "Delivery carton";
const pickup_carton = "Pick-up carton";

View File

@@ -5,7 +5,7 @@ class CargoType {
double weight; double weight;
bool isChecked; bool isChecked;
int qty; int qty;
bool? isCutomDuty; bool isCutomDuty;
double customDutyFee; double customDutyFee;
double calRate; double calRate;
double calWeight; double calWeight;
@@ -21,7 +21,7 @@ class CargoType {
this.calRate = 0, this.calRate = 0,
this.isChecked = false, this.isChecked = false,
this.qty = 0, this.qty = 0,
this.isCutomDuty, this.isCutomDuty = false,
this.customDutyFee = 0}); this.customDutyFee = 0});
factory CargoType.fromMap(Map<String, dynamic> map, String id) { factory CargoType.fromMap(Map<String, dynamic> map, String id) {
@@ -33,8 +33,8 @@ class CargoType {
calWeight: map['cal_weight']?.toDouble() ?? 0, calWeight: map['cal_weight']?.toDouble() ?? 0,
calRate: map['cal_rate']?.toDouble() ?? 0, calRate: map['cal_rate']?.toDouble() ?? 0,
isCutomDuty: map['custom_duty'] ?? false, isCutomDuty: map['custom_duty'] ?? false,
customDutyFee: (map['custom_duty_fee'] ?? 0).toDouble(), customDutyFee: (map['custom_duty_fee'] ?? 0).toDouble());
qty: (map['qty'] ?? 0).toInt()); //qty: (map['qty'] ?? 0).toInt());
} }
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {

View File

@@ -14,6 +14,7 @@ class Package {
DateTime? currentStatusDate; DateTime? currentStatusDate;
List<String> photoUrls; List<String> photoUrls;
List<ShipmentStatus> shipmentHistory; List<ShipmentStatus> shipmentHistory;
List<String> cartonIds;
String? desc; String? desc;
String? status; String? status;
@@ -69,7 +70,8 @@ class Package {
this.deliveryAddress, this.deliveryAddress,
this.isChecked = false, this.isChecked = false,
this.photoFiles = const [], this.photoFiles = const [],
this.senderPhoneNumber}); this.senderPhoneNumber,
this.cartonIds = const []});
factory Package.fromMap(Map<String, dynamic> map, String docID) { factory Package.fromMap(Map<String, dynamic> map, String docID) {
var _currentStatusDate = (map['status_date'] as Timestamp); var _currentStatusDate = (map['status_date'] as Timestamp);
@@ -82,6 +84,9 @@ class Package {
var da = map['delivery_address']; var da = map['delivery_address'];
var _da = da != null ? DeliveryAddress.fromMap(da, da["id"]) : null; var _da = da != null ? DeliveryAddress.fromMap(da, da["id"]) : null;
List<String> cartonIds =
map['carton_ids'] == null ? [] : List.from(map['carton_ids']);
return Package( return Package(
id: docID, id: docID,
userID: map['user_id'], userID: map['user_id'],
@@ -99,7 +104,8 @@ class Package {
deliveryAddress: _da, deliveryAddress: _da,
currentStatusDate: _currentStatusDate.toDate().toLocal(), currentStatusDate: _currentStatusDate.toDate().toLocal(),
photoUrls: _photoUrls, photoUrls: _photoUrls,
shipmentHistory: _shipmentStatus); shipmentHistory: _shipmentStatus,
cartonIds: cartonIds);
} }
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {

View File

@@ -106,7 +106,7 @@ class _CargoTableState extends State<CargoTable> {
padding: const EdgeInsets.only(right: 48.0), padding: const EdgeInsets.only(right: 48.0),
child: Align( child: Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: Text(total.toStringAsFixed(2), child: Text(total.toStringAsFixed(2)+" lb",
style: TextStyle(fontWeight: FontWeight.bold))), style: TextStyle(fontWeight: FontWeight.bold))),
), ),
), ),

View File

@@ -0,0 +1,356 @@
import 'package:fcs/helpers/theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart';
import 'package:provider/provider.dart';
import '../../domain/entities/cargo_type.dart';
import '../../domain/entities/user.dart';
import '../rates/model/shipment_rate_model.dart';
import '../widgets/continue_button.dart';
import '../widgets/display_text.dart';
import '../widgets/local_title.dart';
import '../widgets/previous_button.dart';
import 'custom_duty_addition.dart';
typedef OnPrevious = Function(
List<CargoType> cargoTypes, List<CargoType> customDuties);
typedef OnContinue = Function(
List<CargoType> cargoTypes, List<CargoType> customDuties);
class CargoWidget extends StatefulWidget {
final User sender;
final User consignee;
final List<CargoType> cargoTypes;
final List<CargoType> customDuties;
final OnPrevious? onPrevious;
final OnContinue? onContinue;
const CargoWidget({
Key? key,
required this.cargoTypes,
required this.customDuties,
this.onPrevious,
this.onContinue,
required this.sender,
required this.consignee,
}) : super(key: key);
@override
State<CargoWidget> createState() => _CargoWidgetState();
}
class _CargoWidgetState extends State<CargoWidget> {
List<CargoType> _cargoTypes = [];
List<CargoType> _customDuties = [];
TextEditingController _totalCtl = TextEditingController();
List<TextEditingController> _cargoTypeControllers = [];
@override
void initState() {
_init();
super.initState();
}
_init() {
var model = context.read<ShipmentRateModel>();
_cargoTypes = model.rate.cargoTypes.map((e) => e.clone()).toList();
if (widget.cargoTypes.isNotEmpty) {
} else {
_cargoTypes.forEach((e) {
var editor = new TextEditingController();
editor.text = '';
editor.addListener(inputChangeListener);
_cargoTypeControllers.add(editor);
});
}
if (mounted) {
setState(() {});
}
}
bool isFieldEmpty(int index) {
return _cargoTypeControllers[index].text.isEmpty;
}
List<int> getEmptyFields() {
List<int> emptyFields = [];
for (int i = 0; i < _cargoTypeControllers.length; i++) {
if (isFieldEmpty(i)) {
emptyFields.add(i);
}
}
return emptyFields;
}
inputChangeListener() {
List<int> emptyFields = getEmptyFields();
print("emptyFields:$emptyFields");
// if (emptyFields.isNotEmpty && emptyFields.length == 1) {
// // _cargoTypeControllers[emptyFields.first].text =
// }
if (emptyFields.isEmpty) {
_cargoTypes.asMap().entries.forEach((e) {
_cargoTypes[e.key].weight =
double.tryParse(_cargoTypeControllers[e.key].text) ?? 0;
});
double total = _cargoTypes.fold(0, (sum, value) => sum + value.weight);
setState(() {
_totalCtl.text = total.toString();
});
} else {
// if (emptyFields.length == 1) {
// print("_totalCtl.text:${_totalCtl.text}");
// if (_totalCtl.text.isNotEmpty) {
// double t = double.tryParse(_totalCtl.text) ?? 0;
// _cargoTypes.asMap().entries.forEach((e) {
// _cargoTypes[e.key].weight =
// double.tryParse(_cargoTypeControllers[e.key].text) ?? 0;
// });
// double result =
// _cargoTypes.fold(0, (sum, value) => sum + value.weight);
// double remaining = t - result;
// setState(() {
// _cargoTypeControllers[emptyFields.first].text =
// remaining.toString();
// });
// }
// }
}
}
@override
Widget build(BuildContext context) {
final senderBox = DisplayText(
text: widget.sender.name,
labelTextKey: "box.sender.title",
iconData: MaterialCommunityIcons.account_arrow_right,
subText: Text(widget.sender.fcsID!,
style: TextStyle(fontSize: 13, color: labelColor)),
);
final consigneeBox = DisplayText(
text: widget.consignee.name,
labelTextKey: "box.consignee.title",
iconData: MaterialCommunityIcons.account_arrow_left,
subText: Text(widget.consignee.fcsID!,
style: TextStyle(fontSize: 13, color: labelColor)),
);
final userRow = Row(
children: [
Expanded(
child: senderBox,
flex: 2,
),
Flexible(child: consigneeBox)
],
);
final cargosBox = Wrap(
alignment: WrapAlignment.spaceBetween,
runSpacing: 15,
children: _cargoTypes.asMap().entries.map((e) {
var key = e.key;
var c = e.value;
return SizedBox(
width: MediaQuery.of(context).size.width / 2.3,
child: Row(
children: [
InkResponse(
radius: 25,
onTap: () {
setState(() {
_cargoTypeControllers[key].clear();
});
},
child: Icon(MaterialIcons.clear)),
const SizedBox(width: 10),
Flexible(
child: inputTextFieldWidget(context,
lableText: c.name ?? "",
controller: _cargoTypeControllers[key]
// onChanged: (newValue) {
// setState(() {
// _cargoTypes[key].weight = double.tryParse(newValue) ?? 0;
// });
// double total =
// _cargoTypes.fold(0, (sum, value) => sum + value.weight);
// setState(() {
// _totalCtl.text = total.toString();
// });
// },
),
),
],
),
);
}).toList());
final totalBox = Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
width: MediaQuery.of(context).size.width / 2.3,
child: Row(
children: [
InkResponse(
radius: 25,
onTap: () {
setState(() {
_totalCtl.clear();
});
},
child: Icon(MaterialIcons.clear)),
const SizedBox(width: 10),
Flexible(
child: inputTextFieldWidget(context,
lableText: "Total", controller: _totalCtl),
),
],
)),
],
);
final subchargeItemTitleBox = LocalTitle(
textKey: "box.input_surcharge_item",
trailing: IconButton(
icon: Icon(
Icons.add_circle,
color: primaryColor,
),
onPressed: () async {
List<CargoType>? customList = await Navigator.push<List<CargoType>>(
context,
CupertinoPageRoute(
builder: (context) =>
CustomDutyAddition(customDuties: _customDuties)));
if (customList == null) return;
_customDuties = List.from(customList);
if (mounted) {
setState(() {});
}
}),
);
final subChargeItemsBox = Wrap(
alignment: WrapAlignment.spaceBetween,
runSpacing: 15,
children: _customDuties.asMap().entries.map((e) {
var key = e.key;
var c = e.value;
return SizedBox(
width: MediaQuery.of(context).size.width / 2.3,
child: Row(
children: [
InkResponse(
radius: 25,
onTap: () {
setState(() {
_customDuties.removeAt(key);
});
},
child: Icon(Feather.minus_circle)),
const SizedBox(width: 10),
Flexible(
child: inputTextFieldWidget(
context,
lableText: c.name ?? "",
),
),
],
),
);
}).toList());
final continueBtn = ContinueButton(
onTap: () {
// if (selectedPackageList.isEmpty || searchResults.isEmpty) {
// showMsgDialog(context, 'Error', "Please select the packages");
// return false;
// }
// if (widget.onContinue != null) {
// widget.onContinue!(selectedPackageList);
// }
},
);
final previousBtn = PreviousButton(onTap: () {
if (widget.onPrevious != null) {
widget.onPrevious!(_cargoTypes, _customDuties);
}
});
return Column(
children: [
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 10, right: 10),
child: ListView(
children: [
const SizedBox(height: 8),
userRow,
LocalTitle(textKey: "box.input_cargo_weight", topPadding: 10),
cargosBox,
const SizedBox(height: 15),
Divider(),
const SizedBox(height: 5),
totalBox,
subchargeItemTitleBox,
subChargeItemsBox,
const SizedBox(height: 30),
],
),
),
),
widget.onContinue != null
? Padding(
padding: const EdgeInsets.only(left: 15, right: 15, top: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
previousBtn,
continueBtn,
],
),
)
: const SizedBox(),
const SizedBox(height: 20)
],
);
}
Widget inputTextFieldWidget(BuildContext context,
{required String lableText,
TextEditingController? controller,
Function(String)? onChanged}) {
return TextFormField(
controller: controller,
style: textStyle,
cursorColor: primaryColor,
keyboardType: TextInputType.number,
onChanged: onChanged,
decoration: new InputDecoration(
contentPadding: EdgeInsets.all(0),
labelText: lableText,
labelStyle: newLabelStyle(color: Colors.black54, fontSize: 17),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
disabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
),
);
}
}

View File

@@ -90,7 +90,7 @@ class _CargoTableState extends State<CargoTable> {
), ),
), ),
DataCell( DataCell(
c.isCutomDuty! c.isCutomDuty
? GestureDetector( ? GestureDetector(
onTap: () async { onTap: () async {
String? _t = await showDialog( String? _t = await showDialog(

View File

@@ -14,7 +14,9 @@ import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'mix_cation/mix_cartion_editor.dart'; import '../main/util.dart';
import 'carton_editor_for_package.dart';
import 'mix_carton/mix_carton_editor.dart';
import 'carton_row.dart'; import 'carton_row.dart';
class CartonEditor extends StatefulWidget { class CartonEditor extends StatefulWidget {
@@ -26,7 +28,7 @@ class CartonEditor extends StatefulWidget {
} }
class _CartonEditorState extends State<CartonEditor> { class _CartonEditorState extends State<CartonEditor> {
List<String> cartonTypes = [carton_from_packages, carton_mix_carton]; List<String> _cartonTypes = [carton_from_packages, carton_mix_carton];
List<Carton> _cartons = []; List<Carton> _cartons = [];
bool _isLoading = false; bool _isLoading = false;
@@ -34,23 +36,26 @@ class _CartonEditorState extends State<CartonEditor> {
int _billToValue = 1; int _billToValue = 1;
String? _selectedCartonType; String? _selectedCartonType;
User? consignee; User? _consignee;
User? sender; User? _sender;
Carton? _carton; Carton? _carton;
@override @override
void initState() { void initState() {
_init();
super.initState(); super.initState();
}
_init() {
if (widget.carton != null) { if (widget.carton != null) {
_carton = widget.carton; _carton = widget.carton;
_selectedCartonType = _carton!.cartonType; _selectedCartonType = _carton!.cartonType;
_isNew = false; _isNew = false;
consignee = User( _consignee = User(
id: _carton!.userID, fcsID: _carton!.fcsID, name: _carton!.userName); id: _carton!.userID, fcsID: _carton!.fcsID, name: _carton!.userName);
sender = User( _sender = User(
id: _carton!.senderID, id: _carton!.senderID,
fcsID: _carton!.senderFCSID, fcsID: _carton!.senderFCSID,
name: _carton!.senderName); name: _carton!.senderName);
@@ -62,6 +67,20 @@ class _CartonEditorState extends State<CartonEditor> {
Carton(cartonNumber: "A177(A)-3#2", cartonWeight: 35.5), Carton(cartonNumber: "A177(A)-3#2", cartonWeight: 35.5),
Carton(cartonNumber: "A177(A)-3#1", cartonWeight: 25.5) Carton(cartonNumber: "A177(A)-3#1", cartonWeight: 25.5)
]; ];
_sender = User(
name: "ptd-phyo44 kaelone",
fcsID: "FCS-8X6V",
phoneNumber: "+959444444444",
id: "48u_4s-HiQeW-HwSqeRd9TSMWh3mLZfSk5rpaUEh_zw");
_consignee = User(
id: "HsIwG88K-0_HSazgEy5QR27kcjkOvfv7_Sr1JP18Q1A",
name: "One One",
phoneNumber: "+959111111111",
fcsID: "FCS-EFRF");
}
if (mounted) {
setState(() {});
} }
} }
@@ -107,7 +126,7 @@ class _CartonEditorState extends State<CartonEditor> {
final cartonTypeBox = LocalRadioButtons( final cartonTypeBox = LocalRadioButtons(
readOnly: !_isNew, readOnly: !_isNew,
values: cartonTypes, values: _cartonTypes,
selectedValue: _selectedCartonType, selectedValue: _selectedCartonType,
callback: (String? v) { callback: (String? v) {
setState(() { setState(() {
@@ -125,6 +144,22 @@ class _CartonEditorState extends State<CartonEditor> {
onTap: () { onTap: () {
//for packages //for packages
if (isFromPackages) { if (isFromPackages) {
if (_sender == null) {
showMsgDialog(
context, "Error", "Please select sender's FCS ID");
return;
}
if (_consignee == null) {
showMsgDialog(
context, "Error", "Please select consignee's FCS ID");
return;
}
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => CartonEditorForPackage(
sender: _sender!, consignee: _consignee!)));
} }
// for mix cartion // for mix cartion
else { else {
@@ -153,25 +188,25 @@ class _CartonEditorState extends State<CartonEditor> {
icon: Icon(Icons.search, color: Colors.black), icon: Icon(Icons.search, color: Colors.black),
onPressed: () => searchUser(context, onUserSelect: (u) { onPressed: () => searchUser(context, onUserSelect: (u) {
setState(() { setState(() {
this.consignee = u; this._consignee = u;
}); });
}, popPage: true)), }, popPage: true)),
], ],
); );
final consigneefcsIDBox = DisplayText( final consigneefcsIDBox = DisplayText(
text: consignee != null ? consignee!.fcsID : "", text: _consignee != null ? _consignee!.fcsID : "",
labelTextKey: "processing.fcs.id", labelTextKey: "processing.fcs.id",
icon: FcsIDIcon(), icon: FcsIDIcon(),
); );
final consigneePhoneBox = DisplayText( final consigneePhoneBox = DisplayText(
text: consignee != null ? consignee!.phoneNumber : "", text: _consignee != null ? _consignee!.phoneNumber : "",
labelTextKey: "processing.phone", labelTextKey: "processing.phone",
iconData: MaterialCommunityIcons.phone); iconData: MaterialCommunityIcons.phone);
final consigneeNameBox = DisplayText( final consigneeNameBox = DisplayText(
text: consignee != null ? consignee!.name : "", text: _consignee != null ? _consignee!.name : "",
labelTextKey: "processing.consignee.name", labelTextKey: "processing.consignee.name",
maxLines: 2, maxLines: 2,
iconData: MaterialCommunityIcons.account_arrow_left); iconData: MaterialCommunityIcons.account_arrow_left);
@@ -199,25 +234,25 @@ class _CartonEditorState extends State<CartonEditor> {
icon: Icon(Icons.search, color: Colors.black), icon: Icon(Icons.search, color: Colors.black),
onPressed: () => searchUser(context, onUserSelect: (u) { onPressed: () => searchUser(context, onUserSelect: (u) {
setState(() { setState(() {
this.sender = u; this._sender = u;
}); });
}, popPage: true)), }, popPage: true)),
], ],
); );
final senderIDBox = DisplayText( final senderIDBox = DisplayText(
text: sender != null ? sender!.fcsID : "", text: _sender != null ? _sender!.fcsID : "",
labelTextKey: "processing.fcs.id", labelTextKey: "processing.fcs.id",
icon: FcsIDIcon()); icon: FcsIDIcon());
final senderPhoneBox = DisplayText( final senderPhoneBox = DisplayText(
text: sender != null ? sender!.phoneNumber : "", text: _sender != null ? _sender!.phoneNumber : "",
labelTextKey: "processing.phone", labelTextKey: "processing.phone",
iconData: MaterialCommunityIcons.phone, iconData: MaterialCommunityIcons.phone,
); );
final senderNameBox = DisplayText( final senderNameBox = DisplayText(
text: sender != null ? sender!.name : "", text: _sender != null ? _sender!.name : "",
labelTextKey: "processing.shipper.name", labelTextKey: "processing.shipper.name",
maxLines: 2, maxLines: 2,
iconData: MaterialCommunityIcons.account_arrow_right); iconData: MaterialCommunityIcons.account_arrow_right);

View File

@@ -0,0 +1,239 @@
// ignore_for_file: deprecated_member_use
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import '../../../domain/constants.dart';
import '../../../domain/entities/carton_size.dart';
import '../../../domain/entities/fcs_shipment.dart';
import '../../../domain/vo/local_step.dart';
import '../../../helpers/theme.dart';
import '../../domain/entities/cargo_type.dart';
import '../../domain/entities/package.dart';
import '../../domain/entities/user.dart';
import '../main/util.dart';
import '../widgets/local_text.dart';
import '../widgets/progress.dart';
import '../widgets/step_widget.dart';
import 'cargo_widget.dart';
import 'carton_size_widget.dart';
import 'model/package_selection_model.dart';
import 'package_selection_widget.dart';
class CartonEditorForPackage extends StatefulWidget {
final User sender;
final User consignee;
const CartonEditorForPackage(
{Key? key, required this.sender, required this.consignee})
: super(key: key);
@override
State<CartonEditorForPackage> createState() => _CartonEditorForPackageState();
}
class _CartonEditorForPackageState extends State<CartonEditorForPackage> {
var dateFormatter = DateFormat('dd MMM yyyy');
final NumberFormat numberFormatter = NumberFormat("#,###");
List<LocalStep> steps = [
LocalStep(lable: 'Size', stepType: StepType.SIZE),
LocalStep(lable: 'Packages', stepType: StepType.PACKAGES),
LocalStep(lable: 'Cargos', stepType: StepType.CARGOS),
LocalStep(lable: 'Submit', stepType: StepType.SUBMIT)
];
List<Package> _packages = [];
List<CargoType> _cargoTypes = [];
List<CargoType> _customDuties = [];
int currentStep = 0;
double _length = 0;
double _width = 0;
double _height = 0;
String _selectedDeliveryType = delivery_caton;
FcsShipment? _shipment;
String _cartonSizeType = standardCarton;
CartonSize? _standardSize;
bool _isLoading = false;
@override
void initState() {
context.read<PackageSelectionModel>().clearSelection();
super.initState();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
if (currentStep == 0) {
Navigator.of(context).pop();
}
if (currentStep > 0) {
setState(() {
currentStep -= 1;
});
}
return Future.value(false);
},
child: LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
elevation: 0,
centerTitle: true,
leading: IconButton(
icon: const Icon(CupertinoIcons.back,
color: primaryColor, size: 25),
onPressed: () {
if (currentStep == 0) {
Navigator.of(context).pop();
}
if (currentStep > 0) {
setState(() {
currentStep -= 1;
});
}
},
),
backgroundColor: Colors.white,
title: LocalText(context, 'boxes.new',
color: primaryColor, fontSize: 20),
),
body: Column(
children: [
StepperWidget(
labels: steps.map((e) => e.lable).toList(),
currentStep: currentStep,
eachStepWidth: MediaQuery.of(context).size.width / 4,
onChange: (index) {
if (index > currentStep) {
return;
}
setState(() {
currentStep = index;
});
},
),
getContent(currentStep)
],
))),
);
}
Widget getContent(int index) {
var step = steps[index];
if (step.stepType == StepType.SIZE) {
return Expanded(
child: CartonSizeWidget(
deliveryType: _selectedDeliveryType,
sender: widget.sender,
consignee: widget.consignee,
shipment: _shipment,
cartonSizeType: _cartonSizeType,
standardSize: _standardSize,
length: _length,
width: _width,
height: _height,
onPrevious: () {
Navigator.pop(context);
},
onContinue: (deliveryType, shipment, cartonSizeType,
{standardSize, length, width, height}) {
setState(() {
_selectedDeliveryType = deliveryType;
_shipment = shipment;
_cartonSizeType = cartonSizeType;
_standardSize = standardSize;
_length = length ?? 0;
_width = width ?? 0;
_height = height ?? 0;
currentStep += 1;
});
},
));
} else if (step.stepType == StepType.PACKAGES) {
return Expanded(
child: PackageSelectionWidget(
sender: widget.sender,
consignee: widget.consignee,
shipment: _shipment!,
packages: _packages,
onContinue: (packages) {
setState(() {
_packages = List.from(packages);
currentStep += 1;
});
},
onPrevious: (packages) {
setState(() {
_packages = List.from(packages);
currentStep -= 1;
});
},
),
);
} else if (step.stepType == StepType.CARGOS) {
return Expanded(
child: CargoWidget(
sender: widget.sender,
consignee: widget.consignee,
cargoTypes: _cargoTypes,
customDuties: _customDuties,
onContinue: (cargoTypes, customDuties) {
setState(() {
_cargoTypes = List.from(cargoTypes);
_customDuties = List.from(customDuties);
currentStep += 1;
});
},
onPrevious: (cargoTypes, customDuties) {
setState(() {
_cargoTypes = List.from(cargoTypes);
_customDuties = List.from(customDuties);
currentStep -= 1;
});
},
),
);
} else {
return Expanded(
child: Text("Submit"),
// child: MixCartonSubmit(
// cartonSizeType: _cartonSizeType,
// standardSize: _standardSize,
// length: _length,
// width: _width,
// height: _height,
// shipment: _shipment!,
// cartons: _packages,
// onCreate: () {
// _create();
// },
// onPrevious: () {
// setState(() {
// currentStep -= 1;
// });
// },
// ),
);
}
}
_create() async {
setState(() {
_isLoading = true;
});
try {
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -1,6 +1,8 @@
import 'package:fcs/domain/constants.dart'; import 'package:fcs/domain/constants.dart';
import 'package:fcs/domain/entities/cargo_type.dart';
import 'package:fcs/domain/entities/carton.dart'; import 'package:fcs/domain/entities/carton.dart';
import 'package:fcs/domain/entities/package.dart'; import 'package:fcs/domain/entities/package.dart';
import 'package:fcs/domain/entities/pickup.dart';
import 'package:fcs/domain/vo/delivery_address.dart'; import 'package:fcs/domain/vo/delivery_address.dart';
import 'package:fcs/helpers/theme.dart'; import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/carton_size/model/carton_size_model.dart'; import 'package:fcs/pages/carton_size/model/carton_size_model.dart';
@@ -15,7 +17,10 @@ import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/local_radio_buttons.dart'; import 'package:fcs/pages/widgets/local_radio_buttons.dart';
import 'package:fcs/pages/widgets/local_text.dart'; import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/local_title.dart'; import 'package:fcs/pages/widgets/local_title.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/progress.dart';
import 'package:fcs/pages/widgets/status_tree.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart';
@@ -32,8 +37,10 @@ import 'widgets.dart';
final DateFormat dateFormat = DateFormat("d MMM yyyy"); final DateFormat dateFormat = DateFormat("d MMM yyyy");
class CartonInfo extends StatefulWidget { class CartonInfo extends StatefulWidget {
final Carton? box; final Package? package;
CartonInfo({this.box}); final Carton? box;
CartonInfo({this.box,this.package});
@override @override
_CartonInfoState createState() => _CartonInfoState(); _CartonInfoState createState() => _CartonInfoState();
@@ -42,7 +49,12 @@ class CartonInfo extends StatefulWidget {
class _CartonInfoState extends State<CartonInfo> { class _CartonInfoState extends State<CartonInfo> {
bool _isLoading = false; bool _isLoading = false;
Carton? _box; Carton? _box;
List<CargoType>? cargoTypes;
List<String> pickups=['203PVH','Fh290','HH211'];
Map<String,dynamic> cargos={"General":150,"Electronics":15};
DeliveryAddress? _deliveryAddress = new DeliveryAddress(); DeliveryAddress? _deliveryAddress = new DeliveryAddress();
MultiImgController multiImgController = MultiImgController();
TextEditingController _widthController = new TextEditingController(); TextEditingController _widthController = new TextEditingController();
TextEditingController _heightController = new TextEditingController(); TextEditingController _heightController = new TextEditingController();
TextEditingController _lengthController = new TextEditingController(); TextEditingController _lengthController = new TextEditingController();
@@ -57,11 +69,13 @@ class _CartonInfoState extends State<CartonInfo> {
bool isSmallBag = false; bool isSmallBag = false;
bool isFromCartons = false; bool isFromCartons = false;
bool isEdiable = false; bool isEdiable = false;
Package? _package;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_box = widget.box; _box = widget.box;
initPackage(widget.package);
//for shipment weight //for shipment weight
volumetricRatio = Provider.of<ShipmentRateModel>(context, listen: false) volumetricRatio = Provider.of<ShipmentRateModel>(context, listen: false)
.rate .rate
@@ -74,6 +88,14 @@ class _CartonInfoState extends State<CartonInfo> {
_loadPackages(); _loadPackages();
_loadMixCartons(); _loadMixCartons();
} }
initPackage(Package? package) {
if (package == null) return;
multiImgController.setImageUrls = package.photoUrls;
setState(() {
_package = package;
});
}
_updateBoxData() { _updateBoxData() {
_widthController.text = _box!.width.toString(); _widthController.text = _box!.width.toString();
@@ -176,14 +198,14 @@ class _CartonInfoState extends State<CartonInfo> {
//iconData: Ionicons.ios_airplane, //iconData: Ionicons.ios_airplane,
); );
final cartonQrBox = DisplayText( final cartonQrBox = DisplayText(
// text: _box!., // text: _box!.,
//labelTextKey: "box.number", //labelTextKey: "box.number",
iconData: AntDesign.qrcode, iconData: AntDesign.qrcode,
); );
final shipmentBox = DisplayText( final shipmentBox = DisplayText(
text: _box!.fcsShipmentNumber, text: _box!.fcsShipmentNumber,
labelTextKey: "box.fcs_shipment_num", labelTextKey: "box.fcs_shipment_num",
// iconData: Ionicons.ios_airplane, // iconData: Ionicons.ios_airplane,
); );
final deliveryBox = DisplayText( final deliveryBox = DisplayText(
text: "Delivery Carton", text: "Delivery Carton",
@@ -199,8 +221,9 @@ class _CartonInfoState extends State<CartonInfo> {
final customerNameBox = DisplayText( final customerNameBox = DisplayText(
text: _box!.userName == null ? "" : _box!.userName, text: _box!.userName == null ? "" : _box!.userName,
text1: _box!.fcsID == null ? "" : _box!.fcsID, subText: Text(_box!.fcsID ?? "", style: textStyle),
labelTextKey: "box.name", labelTextKey: "box.name",
//iconData: Icons.person, //iconData: Icons.person,
); );
@@ -211,18 +234,22 @@ class _CartonInfoState extends State<CartonInfo> {
); );
final consigneeNameBox = DisplayText( final consigneeNameBox = DisplayText(
text: _box!.userName != null ? _box!.userName : "", text: _box!.senderName != null ? _box!.senderName : "",
text1: _box!.fcsID != null ? _box!.fcsID : "", subText: Text(_box!.senderFCSID ?? "", style: textStyle),
labelTextKey: "processing.consignee.name", labelTextKey: "processing.consignee.name",
//maxLines: 2, //maxLines: 2,
//iconData: Ionicons.document_text_outline, // iconData: Ionicons.document_text_outline,
); );
final consigneeBox = Container( final consigneeBox = Container(
child: Column( child: Column(
children: [ children: [
consigneefcsIDBox,
consigneeNameBox, consigneeNameBox,
IconButton(icon:Icon(Ionicons.document_text_outline),
onPressed:() {},),
Text("Bill to",style:TextStyle(color:Color.fromARGB(255, 57, 80, 233)))
], ],
), ),
); );
@@ -282,11 +309,22 @@ class _CartonInfoState extends State<CartonInfo> {
SizedBox(child: heightBox, width: 80), SizedBox(child: heightBox, width: 80),
], ],
); );
final packageBox = DisplayText( final packageBox = DisplayText(
text: "203FVH", //text: "203FVH",
labelTextKey: "box.package", labelTextKey: "box.package",
//iconData: AntDesign.CodeSandbox,
); );
final img = MultiImageFile(
enabled: false,
controller: multiImgController,
title: "Receipt File",
);
final cargoBox = DisplayText(
//text: "203FVH",
labelTextKey: "box.cargo.type",
);
final cartonSizeBox = DisplayText( final cartonSizeBox = DisplayText(
text: _cartonSizeController.text, text: _cartonSizeController.text,
@@ -353,9 +391,9 @@ class _CartonInfoState extends State<CartonInfo> {
] ]
: [], : [],
), ),
body: Padding( body: Container(
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
child: ListView(shrinkWrap: true, children: <Widget>[ child: Row( children: <Widget>[
// Container(child: Row(children: [ // Container(child: Row(children: [
// Column(children: [ // Column(children: [
// LocalTitle(textKey: "box.type.title"), // LocalTitle(textKey: "box.type.title"),
@@ -365,51 +403,116 @@ class _CartonInfoState extends State<CartonInfo> {
// ],) // ],)
// ]),), // ]),),
Padding(padding: EdgeInsets.only(left: 30), Padding(
child: padding: EdgeInsets.only(left: 10),
Row( child: Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [Expanded(child: cartonTypeBox,
flex: 1,
),
Flexible(
child: cartonQrBox,
),
],)),
Padding(padding: EdgeInsets.only(left: 30),
child:
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [Expanded(child: shipmentBox,
flex: 1,
),
Flexible(
child: deliveryBox,
),
],)),
Padding(padding: EdgeInsets.only(left: 30),
child:
Row(
children: [
Flexible(child: customerNameBox,
),
Flexible(
child: consigneeNameBox,
),
Flexible(child:
Column(
children: [ children: [
Icon(Ionicons.document_text_outline), Expanded(
Text("Bill to",style:TextStyle(color:Colors.blue)) child: cartonTypeBox,
] flex: 1,
),
Expanded(
child: IconButton(
alignment: Alignment.centerLeft,
iconSize: 30,
icon: const Icon( AntDesign.qrcode),
onPressed: () {
// ...
},
),
),
],
)), )),
],)),
Padding(
padding: EdgeInsets.only(left: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
child: shipmentBox,
flex: 1,
),
Flexible(
child: deliveryBox,
),
],
)),
Padding(
padding: EdgeInsets.only(left: 10),
child: Row(
children: [
Flexible(
child: customerNameBox,
),
Flexible(
child: consigneeNameBox,
),
Flexible(
child: Column(children: [
Icon(Ionicons.document_text_outline),
Text("Bill to", style: TextStyle(color: Colors.blue))
])),
],
)),
Padding(padding: EdgeInsets.only(left:10),
child: Flexible(child: packageBox)),
for(int pack=0;pack<pickups.length;pack++)
Padding(padding: EdgeInsets.only(left: 10.0),
child:
Text(pickups[pack],style:TextStyle(fontSize: 15,color: Colors.black))
),
Padding(padding: EdgeInsets.only(left: 10),
child: cargoBox,),
// ListView.builder(
// itemCount: cargos.length,
// itemBuilder: (BuildContext context, int index) {
// return ListTile(
// title: Text(cargos[index].key));
// }),
Expanded(child:
Padding(padding: EdgeInsets.only(left: 200.0,right: 8.0),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xff272262),
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)),
minimumSize: Size(100, 35), //////// HERE
),
onPressed: (){},
child: const Text('Upload Images'),
),)),
Center(child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color.fromARGB(255, 196, 39, 52),
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)),
minimumSize: Size(300, 45), //////// HERE
),
onPressed: (){},
child: const Text('Delete Carton',style:TextStyle(fontSize: 20)),
),),
//_package!.photoUrls.length == 0 || _package!.photoUrls.isEmpty ? Container() : img,
// Padding(padding: EdgeInsets.only(left: 10.0),
// child: Column(children: [
// cargos.isEmpty?Container():
// Text(cargos[cargo].key),
// Text(cargos[cargo].value),
// ]),),
//_packageList,
//LocalTitle(textKey: "box.shipment_info"), //LocalTitle(textKey: "box.shipment_info"),
// shipmentBox, // shipmentBox,
// isSmallBag ? mixCartonNumberBox : Container(), // isSmallBag ? mixCartonNumberBox : Container(),
// isMixBox // isMixBox
// ? Container() // ? Container()
@@ -429,40 +532,49 @@ class _CartonInfoState extends State<CartonInfo> {
// ], // ],
// ) // )
// : Container(), // : Container(),
Padding(padding: EdgeInsets.only(left: 30), // Padding(padding: EdgeInsets.only(left: 10),
child: // child: Row(children:[
packageBox),
isMixBox ? mixTypeBox : Container(), // ListView.builder(
isMixBox ? LocalTitle(textKey: "box.mix_caton_title") : Container(), // itemCount: pickups.length,
isMixBox // itemBuilder: (context, index) {
? Column(children: _getCartons(context, _box!.mixCartons)) // return ListTile(
: Container(), // title: Text(pickups[index]),
isFromPackages || isSmallBag // );
? CartonPackageTable( // },)
packages: _box!.packages,
) // ])),
: Container(), // // isMixBox ? mixTypeBox : Container(),
// isMixBox ? Container() : LocalTitle(textKey: "box.cargo.type"), // isMixBox ? LocalTitle(textKey: "box.mix_caton_title") : Container(),
isMixBox ? Container() : cargoTableBox, // isMixBox
...(isFromPackages || isFromCartons // ? Column(children: _getCartons(context, _box!.mixCartons))
? [ // : Container(),
LocalTitle(textKey: "box.dimension"), // isFromPackages || isSmallBag
cartonSizeBox, // ? CartonPackageTable(
dimBox, // packages: _box!.packages,
] // )
: []), // : Container(),
isMixBox // isMixBox ? Container() : LocalTitle(textKey: "box.cargo.type"),
? Container() // isMixBox ? Container() : cargoTableBox,
: LocalTitle(textKey: "box.delivery_address"), // ...(isFromPackages || isFromCartons
isMixBox // ? [
? Container() // LocalTitle(textKey: "box.dimension"),
: DefaultDeliveryAddress( // cartonSizeBox,
deliveryAddress: _deliveryAddress, // dimBox,
labelKey: "box.delivery_address", // ]
), // : []),
SizedBox( // isMixBox
height: 20, // ? Container()
) // : LocalTitle(textKey: "box.delivery_address"),
// isMixBox
// ? Container()
// : DefaultDeliveryAddress(
// deliveryAddress: _deliveryAddress,
// labelKey: "box.delivery_address",
// ),
// SizedBox(
// height: 20,
// )
]), ]),
), ),
), ),
@@ -515,4 +627,18 @@ class _CartonInfoState extends State<CartonInfo> {
}); });
} }
} }
Widget _packageList(BuildContext context) {
return ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: pickups.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
//color: Colors.amber[pickups[index]],
child: Center(child: Text('Entry ${pickups[index]}')),
);
},
//separatorBuilder: (BuildContext context, int index) => const Divider(),
);
}
} }

View File

@@ -0,0 +1,400 @@
import 'package:fcs/pages/widgets/local_radio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:provider/provider.dart';
import '../../../domain/constants.dart';
import '../../../domain/entities/carton_size.dart';
import '../../../domain/entities/fcs_shipment.dart';
import '../../../helpers/theme.dart';
import '../../domain/entities/user.dart';
import '../carton_size/model/carton_size_model.dart';
import '../fcs_shipment/model/fcs_shipment_model.dart';
import '../main/util.dart';
import '../widgets/box_size_picker.dart';
import '../widgets/continue_button.dart';
import '../widgets/display_text.dart';
import '../widgets/local_dropdown.dart';
import '../widgets/local_radio_buttons.dart';
import '../widgets/local_text.dart';
import '../widgets/local_title.dart';
import '../widgets/previous_button.dart';
typedef OnPrevious = Function();
typedef OnContinue = Function(String deliveryType,FcsShipment shipment, String cartonSizeType,
{CartonSize? standardSize, double? length, double? width, double? height});
class CartonSizeWidget extends StatefulWidget {
final OnPrevious? onPrevious;
final OnContinue? onContinue;
final User sender;
final User consignee;
final String deliveryType;
final FcsShipment? shipment;
final String cartonSizeType;
final CartonSize? standardSize;
final double? length;
final double? width;
final double? height;
const CartonSizeWidget(
{Key? key,
this.onPrevious,
this.onContinue,
this.shipment,
required this.cartonSizeType,
this.standardSize,
this.length,
this.width,
this.height,
required this.sender,
required this.consignee,
required this.deliveryType})
: super(key: key);
@override
State<CartonSizeWidget> createState() => _CartonSizeWidgetState();
}
class _CartonSizeWidgetState extends State<CartonSizeWidget> {
List<String> _deliveryTypes = [delivery_caton, pickup_carton];
FcsShipment? _shipment;
String _cartionSizeType = standardCarton;
List<FcsShipment> _shipments = [];
CartonSize? _selectStandardSize;
late String _selectedDeliveryType;
TextEditingController _widthController = new TextEditingController();
TextEditingController _heightController = new TextEditingController();
TextEditingController _lengthController = new TextEditingController();
@override
void initState() {
_init();
super.initState();
}
_init() async {
_selectedDeliveryType = widget.deliveryType;
_shipment = widget.shipment;
_cartionSizeType = widget.cartonSizeType;
List<CartonSize> cartonSizes = context.read<CartonSizeModel>().cartonSizes;
_selectStandardSize = widget.standardSize ?? cartonSizes.first;
_lengthController.text =
widget.length == null ? "0" : widget.length.toString();
_widthController.text =
widget.width == null ? "0" : widget.width.toString();
_heightController.text =
widget.height == null ? "0" : widget.height.toString();
var fcsShipments =
await context.read<FcsShipmentModel>().getActiveFcsShipments();
_shipments = fcsShipments;
if (mounted) {
setState(() {});
}
}
@override
Widget build(BuildContext context) {
List<CartonSize> cartonSizes = context.watch<CartonSizeModel>().cartonSizes;
bool isStandardSize = _cartionSizeType == standardCarton;
bool isCustomSize = _cartionSizeType == customCarton;
bool isNoneDefinedSize = _cartionSizeType == packageCartion;
final senderBox = DisplayText(
text: widget.sender.name,
labelTextKey: "box.sender.title",
iconData: MaterialCommunityIcons.account_arrow_right,
subText: Text(widget.sender.fcsID!,
style: TextStyle(fontSize: 13, color: labelColor)),
);
final consigneeBox = DisplayText(
text: widget.consignee.name,
labelTextKey: "box.consignee.title",
iconData: MaterialCommunityIcons.account_arrow_left,
subText: Text(widget.consignee.fcsID!,
style: TextStyle(fontSize: 13, color: labelColor)),
);
final userRow = Row(
children: [
Expanded(
child: senderBox,
flex: 2,
),
Flexible(child: consigneeBox)
],
);
final deliveryTypeBox = LocalRadioButtons(
values: _deliveryTypes,
selectedValue: _selectedDeliveryType,
callback: (String? v) {
setState(() {
_selectedDeliveryType = v!;
});
});
final continueBtn = ContinueButton(onTap: () {
double l = double.tryParse(_lengthController.text) ?? 0;
double w = double.tryParse(_widthController.text) ?? 0;
double h = double.tryParse(_heightController.text) ?? 0;
if (_shipment == null) {
showMsgDialog(context, "Error", "Please select shipment");
return;
}
if (isStandardSize &&
_selectStandardSize == null &&
!isCustomSize &&
!isNoneDefinedSize) {
showMsgDialog(
context, "Error", "Please select the standard carton size");
return;
}
if (isCustomSize &&
!isStandardSize &&
!isNoneDefinedSize &&
(l == 0 || w == 0 || h == 0)) {
showMsgDialog(context, "Error", "Please add the carton size");
return;
}
if (widget.onContinue != null) {
widget.onContinue!(_selectedDeliveryType,_shipment!, _cartionSizeType,
standardSize: _selectStandardSize, length: l, width: w, height: h);
}
});
final previousBtn = PreviousButton(onTap: () {
if (widget.onPrevious != null) {
widget.onPrevious!();
}
});
final standardSizeBox = Padding(
padding: const EdgeInsets.only(left: 34.0, top: 8),
child: IgnorePointer(
ignoring: !isStandardSize,
child: DropdownButton<CartonSize>(
isDense: true,
value: _selectStandardSize,
style: TextStyle(color: Colors.black, fontSize: 14),
underline: Container(height: 1, color: Colors.grey),
onChanged: (newValue) {
setState(() {
_selectStandardSize = newValue!;
});
},
isExpanded: true,
items:
cartonSizes.map<DropdownMenuItem<CartonSize>>((CartonSize value) {
return DropdownMenuItem<CartonSize>(
value: value,
child: Row(
children: [
Text("${value.name} - ",
style: TextStyle(
color: isStandardSize ? Colors.black : labelColor)),
Text(
"${value.length.toInt()}”x${value.width.toInt()}”x${value.height.toInt()}",
style: TextStyle(color: labelColor)),
],
),
);
}).toList(),
),
),
);
final lengthBox = BoxSizePicker(
lableKey: 'box.length',
controller: _lengthController,
enable: isCustomSize);
final widthBox = BoxSizePicker(
lableKey: 'box.width',
controller: _widthController,
enable: isCustomSize);
final heightBox = BoxSizePicker(
lableKey: 'box.height',
controller: _heightController,
enable: isCustomSize);
final customSizeBox = Padding(
padding: const EdgeInsets.only(left: 34.0),
child: Row(
children: [
Flexible(child: lengthBox),
Flexible(child: widthBox),
Flexible(child: heightBox)
],
),
);
final cartonSizedBox = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// standard carton size
InkWell(
onTap: () {
setState(() {
_cartionSizeType = standardCarton;
});
},
child: Row(children: <Widget>[
LocalRadio(
value: standardCarton,
groupValue: _cartionSizeType,
onChanged: (p0) {
setState(() {
_cartionSizeType = standardCarton;
});
},
),
Flexible(
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: LocalText(context, 'box.standard_carton_size',
fontSize: 15,
color: isStandardSize ? primaryColor : labelColor),
),
)
]),
),
standardSizeBox,
const SizedBox(height: 20),
// custom size
InkWell(
onTap: () {
setState(() {
_cartionSizeType = customCarton;
});
},
child: Row(children: <Widget>[
LocalRadio(
value: customCarton,
groupValue: _cartionSizeType,
onChanged: (p0) {
setState(() {
_cartionSizeType = customCarton;
});
},
),
Flexible(
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: LocalText(context, 'box.custom_size',
fontSize: 15,
color: isCustomSize ? primaryColor : Colors.black54),
),
)
]),
),
customSizeBox,
const SizedBox(height: 10),
// not defined size
InkWell(
onTap: () {
setState(() {
_cartionSizeType = packageCartion;
});
},
child: Row(children: <Widget>[
LocalRadio(
value: packageCartion,
groupValue: _cartionSizeType,
onChanged: (p0) {
setState(() {
_cartionSizeType = packageCartion;
});
},
),
Flexible(
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: LocalText(context, 'box.package_size',
fontSize: 15,
color: isNoneDefinedSize ? primaryColor : Colors.black54),
),
)
]),
),
Padding(
padding: const EdgeInsets.only(left: 34.0),
child: Text(
"No defined size",
style: TextStyle(
fontSize: 14,
color: isNoneDefinedSize ? Colors.black : labelColor),
),
)
],
);
final fcsShipmentsBox = Container(
padding: EdgeInsets.only(top: 10),
child: LocalDropdown<FcsShipment>(
callback: (v) {
setState(() {
_shipment = v;
});
},
labelKey: "box.shipment",
iconData: Ionicons.ios_airplane,
display: (u) => u.shipmentNumber,
selectedValue: _shipment,
values: _shipments,
));
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ListView(
padding: EdgeInsets.only(left: 10, right: 10),
children: [
const SizedBox(height: 8),
userRow,
LocalTitle(textKey: "box.select.delivery", topPadding: 10),
const SizedBox(height: 5),
deliveryTypeBox,
const SizedBox(height: 5),
LocalTitle(textKey: "box.select_carton_size"),
const SizedBox(height: 8),
cartonSizedBox,
const SizedBox(height: 8),
LocalTitle(textKey: "box.select_shipment"),
const SizedBox(height: 5),
fcsShipmentsBox,
const SizedBox(height: 30)
],
)),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
widget.onPrevious == null ? const SizedBox() : previousBtn,
continueBtn
// warehouse != null ? continueBtn : const SizedBox(),
],
),
),
const SizedBox(
height: 20,
),
],
);
}
}

View File

@@ -0,0 +1,127 @@
import 'package:fcs/domain/entities/cargo_type.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/rates/model/shipment_rate_model.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/local_title.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../widgets/local_button.dart';
class CustomDutyAddition extends StatefulWidget {
final List<CargoType> customDuties;
const CustomDutyAddition({super.key, required this.customDuties});
@override
_CustomDutyAdditionState createState() => _CustomDutyAdditionState();
}
class _CustomDutyAdditionState extends State<CustomDutyAddition> {
bool _isLoading = false;
List<CargoType> customDuties = [];
@override
void initState() {
_init();
super.initState();
}
_init() {
var shipmentRateModel =
Provider.of<ShipmentRateModel>(context, listen: false);
customDuties =
shipmentRateModel.rate.customDuties.map((e) => e.clone()).toList();
for (var p in customDuties) {
if (widget.customDuties.any((e) => e.id == p.id)) {
p.isChecked = true;
} else {
p.isChecked = false;
}
}
if (mounted) {
setState(() {});
}
}
@override
Widget build(BuildContext context) {
List<Widget> getCargoRowList(List<CargoType> _c) {
return _c.map((c) {
return Container(
child: Container(
padding:
EdgeInsets.only(left: 10.0, right: 5.0, top: 3.0, bottom: 3.0),
child: InkWell(
onTap: () {
setState(() {
c.isChecked = !c.isChecked;
});
},
child: Row(
children: <Widget>[
Checkbox(
value: c.isChecked,
activeColor: primaryColor,
onChanged: (bool? check) {
setState(() {
c.isChecked = check ?? false;
});
}),
new Text(c.name ?? '', style: textStyle),
],
),
),
),
);
}).toList();
}
final saveBtn = Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: LocalButton(
textKey: "box.cargo.select.btn",
callBack: () {
List<CargoType> _cargos =
customDuties.where((c) => c.isChecked).toList();
if (_cargos.isEmpty) {
showMsgDialog(context, 'Error', "Please select the cargo type");
return;
}
Navigator.pop(context, _cargos);
},
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: LocalAppBar(
labelKey: 'box.select.cargo_type',
backgroundColor: Colors.white,
labelColor: primaryColor,
arrowColor: primaryColor),
body: Container(
padding: EdgeInsets.all(10),
child: ListView(
shrinkWrap: true,
children: <Widget>[
LocalTitle(
textKey: "box.select.cargo.title",
),
Column(
children: getCargoRowList(customDuties),
),
SizedBox(height: 30),
saveBtn,
SizedBox(height: 20),
],
),
),
),
);
}
}

View File

@@ -36,7 +36,7 @@ class CartonSelectionResult extends StatelessWidget {
var model = context.watch<CartonSelectionModel>(); var model = context.watch<CartonSelectionModel>();
List<Carton> searchResults = model.cartons; List<Carton> searchResults = model.cartons;
return searchResults.isEmpty return searchResults.isEmpty && !model.isLoading
? Center( ? Center(
child: LocalText(context, 'box.no_carton', child: LocalText(context, 'box.no_carton',
color: Colors.black, fontSize: 15)) color: Colors.black, fontSize: 15))

View File

@@ -1,6 +1,6 @@
import 'package:fcs/domain/entities/fcs_shipment.dart'; import 'package:fcs/domain/entities/fcs_shipment.dart';
import 'package:fcs/helpers/theme.dart'; import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/carton/mix_cation/carton_selection_result.dart'; import 'package:fcs/pages/carton/mix_carton/carton_selection_result.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View File

@@ -11,11 +11,13 @@ import '../../../domain/entities/carton_size.dart';
import '../../../domain/entities/fcs_shipment.dart'; import '../../../domain/entities/fcs_shipment.dart';
import '../../../domain/vo/local_step.dart'; import '../../../domain/vo/local_step.dart';
import '../../../helpers/theme.dart'; import '../../../helpers/theme.dart';
import '../../main/util.dart';
import '../../widgets/local_text.dart'; import '../../widgets/local_text.dart';
import '../../widgets/progress.dart'; import '../../widgets/progress.dart';
import '../../widgets/step_widget.dart'; import '../../widgets/step_widget.dart';
import '../model/carton_selection_model.dart'; import '../model/carton_selection_model.dart';
import 'carton_selection_widget.dart'; import 'carton_selection_widget.dart';
import 'mix_carton_submit.dart';
import 'type_widget.dart'; import 'type_widget.dart';
class MixCartonEditor extends StatefulWidget { class MixCartonEditor extends StatefulWidget {
@@ -35,7 +37,7 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
LocalStep(lable: 'Cartons', stepType: StepType.CARTONS), LocalStep(lable: 'Cartons', stepType: StepType.CARTONS),
LocalStep(lable: 'Submit', stepType: StepType.SUBMIT) LocalStep(lable: 'Submit', stepType: StepType.SUBMIT)
]; ];
List<Carton> _cartions = []; List<Carton> _cartons = [];
int currentStep = 0; int currentStep = 0;
double _length = 0; double _length = 0;
@@ -43,7 +45,7 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
double _height = 0; double _height = 0;
FcsShipment? _shipment; FcsShipment? _shipment;
String _cartionSizeType = standardCarton; String _cartonSizeType = standardCarton;
CartonSize? _standardSize; CartonSize? _standardSize;
bool _isLoading = false; bool _isLoading = false;
@@ -97,7 +99,7 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
StepperWidget( StepperWidget(
labels: steps.map((e) => e.lable).toList(), labels: steps.map((e) => e.lable).toList(),
currentStep: currentStep, currentStep: currentStep,
eachStepWidth: 120, eachStepWidth: MediaQuery.of(context).size.width / 3,
onChange: (index) { onChange: (index) {
if (index > currentStep) { if (index > currentStep) {
return; return;
@@ -119,7 +121,7 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
return Expanded( return Expanded(
child: TypeWidget( child: TypeWidget(
shipment: _shipment, shipment: _shipment,
cartonSizeType: _cartionSizeType, cartonSizeType: _cartonSizeType,
standardSize: _standardSize, standardSize: _standardSize,
length: _length, length: _length,
width: _width, width: _width,
@@ -131,7 +133,7 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
{standardSize, length, width, height}) { {standardSize, length, width, height}) {
setState(() { setState(() {
_shipment = shipment; _shipment = shipment;
_cartionSizeType = cartonSizeType; _cartonSizeType = cartonSizeType;
_standardSize = standardSize; _standardSize = standardSize;
_length = length ?? 0; _length = length ?? 0;
_width = width ?? 0; _width = width ?? 0;
@@ -144,16 +146,16 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
return Expanded( return Expanded(
child: CartonSelectionWidget( child: CartonSelectionWidget(
shipment: _shipment!, shipment: _shipment!,
cartons: _cartions, cartons: _cartons,
onContinue: (cartons) { onContinue: (cartons) {
setState(() { setState(() {
_cartions = List.from(cartons); _cartons = List.from(cartons);
currentStep += 1; currentStep += 1;
}); });
}, },
onPrevious: (cartons) { onPrevious: (cartons) {
setState(() { setState(() {
_cartions = List.from(cartons); _cartons = List.from(cartons);
currentStep -= 1; currentStep -= 1;
}); });
}, },
@@ -161,27 +163,39 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
); );
} else { } else {
return Expanded( return Expanded(
child: Text("Submit"), child: MixCartonSubmit(
// child: StockAdjustmentSubmit( cartonSizeType: _cartonSizeType,
// warehouse: _warehouse?.name, standardSize: _standardSize,
// products: products, length: _length,
// onCreate: () { width: _width,
// if (user != null && user.hasInventoryCreate()) { height: _height,
// showConfirmDialog(context, 'stock_adjustment_confirm', _create); shipment: _shipment!,
// } else { cartons: _cartons,
// showDialog( onCreate: () {
// context: context, _create();
// builder: (BuildContext context) => const AuthorizedDialog( },
// uiFunction: funcInventoriesCreate)); onPrevious: () {
// } setState(() {
// }, currentStep -= 1;
// onPrevious: () { });
// setState(() { },
// currentStep -= 1; ),
// });
// },
// ),
); );
} }
} }
_create() async {
setState(() {
_isLoading = true;
});
try {
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
} }

View File

@@ -0,0 +1,289 @@
import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:intl/intl.dart';
import '../../../domain/constants.dart';
import '../../../domain/entities/cargo_type.dart';
import '../../../domain/entities/carton.dart';
import '../../../domain/entities/carton_size.dart';
import '../../../domain/entities/fcs_shipment.dart';
import '../../../helpers/theme.dart';
import '../../widgets/local_text.dart';
import '../../widgets/previous_button.dart';
import '../../widgets/submit_text_widget.dart';
import "package:collection/collection.dart";
typedef OnCreateMixCarton = Function();
typedef OnPrevious = Function();
class MixCartonSubmit extends StatefulWidget {
final FcsShipment shipment;
final List<Carton> cartons;
final String cartonSizeType;
final CartonSize? standardSize;
final double length;
final double width;
final double height;
final OnCreateMixCarton? onCreate;
final OnPrevious? onPrevious;
const MixCartonSubmit(
{Key? key,
this.onCreate,
this.onPrevious,
required this.shipment,
this.cartons = const [],
this.standardSize,
required this.cartonSizeType,
this.length = 0,
this.width = 0,
this.height = 0})
: super(key: key);
@override
State<MixCartonSubmit> createState() => _MixCartonSubmitState();
}
class _MixCartonSubmitState extends State<MixCartonSubmit> {
final NumberFormat numberFormatter = NumberFormat("#,###");
Map<String?, double> _mapCargosByWeight = {};
Map<String?, double> _mapCargosByCustomDutyFee = {};
@override
void initState() {
_init();
super.initState();
}
_init() {
List<CargoType> _cargoTypes = [];
for (var c in widget.cartons) {
_cargoTypes.addAll(c.cargoTypes);
}
// get cargos by weight
Map<String?, List<CargoType>> _cargosByWeight =
groupCargos(_cargoTypes.where((e) => !e.isCutomDuty).toList());
_cargosByWeight.forEach((key, value) {
double total = value.fold(0, (sum, item) => sum + item.weight);
_mapCargosByWeight[key] = total;
});
// get cargos by custom duty fee
Map<String?, List<CargoType>> _cargosByCustomDutyFee =
groupCargos(_cargoTypes.where((e) => e.isCutomDuty).toList());
_cargosByCustomDutyFee.forEach((key, value) {
double total = value.fold(0, (sum, item) => sum + item.qty);
_mapCargosByCustomDutyFee[key] = total;
});
if (mounted) {
setState(() {});
}
}
Map<String?, List<CargoType>> groupCargos(List<CargoType> cargos) {
var groups = groupBy(cargos, (CargoType e) {
String? _categoryName = e.name;
return _categoryName;
});
return groups;
}
@override
Widget build(BuildContext context) {
String? boxDimension = widget.cartonSizeType == standardCarton
? "${widget.standardSize?.name} - ${widget.standardSize?.length.toInt()}”x${widget.standardSize?.width.toInt()}”x${widget.standardSize?.height.toInt()}"
: widget.cartonSizeType == customCarton
? "${widget.length.toInt()}”x${widget.width.toInt()}”x${widget.height.toInt()}"
: null;
final cartonType = Padding(
padding: const EdgeInsets.only(top: 10),
child: SubmitTextWidget(
labelKey: 'box.carton.type',
text: 'Mix Carton',
subText: boxDimension,
),
);
final shipmentBox = Padding(
padding: const EdgeInsets.only(top: 10),
child: SubmitTextWidget(
labelKey: 'box.shipment',
text: widget.shipment.shipmentNumber ?? '',
subText: widget.shipment.status ?? "",
),
);
final cartonsBox = Padding(
padding: const EdgeInsets.only(top: 10),
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Padding(
padding: const EdgeInsets.only(left: 5, bottom: 5),
child: LocalText(context, 'box.cartion.count',
translationVariables: [widget.cartons.length.toString()],
color: primaryColor,
fontSize: 16,
fontWeight: FontWeight.normal),
),
Container(
decoration: BoxDecoration(
border: Border.all(color: primaryColor),
borderRadius: BorderRadius.circular(5),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Wrap(
spacing: 15,
children: widget.cartons.map((e) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2.5,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: Text(
e.cartonNumber ?? "",
style: TextStyle(color: Colors.black, fontSize: 15),
),
),
);
}).toList()),
),
),
]),
);
final cargosBox = Padding(
padding: const EdgeInsets.only(top: 10),
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Padding(
padding: const EdgeInsets.only(left: 5, bottom: 5),
child: LocalText(context, 'box.cargo.type',
color: primaryColor, fontSize: 16, fontWeight: FontWeight.normal),
),
Container(
decoration: BoxDecoration(
border: Border.all(color: primaryColor),
borderRadius: BorderRadius.circular(5),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _mapCargosByWeight.entries.map((e) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
e.key ?? "",
style:
TextStyle(color: Colors.black, fontSize: 15),
),
Text("${numberFormatter.format(e.value)} lb",
style: TextStyle(
color: Colors.black, fontSize: 15))
],
),
);
}).toList()),
const SizedBox(height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _mapCargosByCustomDutyFee.entries.map((e) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
e.key ?? "",
style: TextStyle(color: labelColor, fontSize: 15),
),
Text("${numberFormatter.format(e.value)} pc",
style:
TextStyle(color: labelColor, fontSize: 15))
],
),
);
}).toList()),
],
),
),
),
]),
);
final createBtn = InkWell(
onTap: () {
if (widget.onCreate != null) {
widget.onCreate!();
}
},
child: Container(
alignment: Alignment.bottomRight,
height: 45,
width: 150,
decoration: BoxDecoration(
color: primaryColor,
borderRadius: BorderRadius.circular(5),
),
child: TextButton(
onPressed: null,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
child: LocalText(context, 'box.crete.carton',
color: Colors.white, fontSize: 15),
),
const SizedBox(width: 5),
const Icon(
MaterialCommunityIcons.check_circle_outline,
color: Colors.white,
),
],
),
),
));
final previousBtn = PreviousButton(onTap: () {
if (widget.onPrevious != null) {
widget.onPrevious!();
}
});
return Column(
children: [
Expanded(
child: ListView(
padding: const EdgeInsets.all(20),
children: [
cartonType,
const SizedBox(height: 10),
shipmentBox,
const SizedBox(height: 10),
widget.cartons.isNotEmpty ? cartonsBox : const SizedBox(),
const SizedBox(height: 10),
cargosBox,
const SizedBox(height: 20),
],
),
),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [previousBtn, createBtn],
),
),
const SizedBox(height: 20)
],
);
}
}

View File

@@ -109,7 +109,7 @@ class _TypeWidgetState extends State<TypeWidget> {
!isCustomSize && !isCustomSize &&
!isNoneDefinedSize) { !isNoneDefinedSize) {
showMsgDialog( showMsgDialog(
context, "Error", "Please select the standard cartion size"); context, "Error", "Please select the standard carton size");
return; return;
} }
@@ -117,7 +117,7 @@ class _TypeWidgetState extends State<TypeWidget> {
!isStandardSize && !isStandardSize &&
!isNoneDefinedSize && !isNoneDefinedSize &&
(l == 0 || w == 0 || h == 0)) { (l == 0 || w == 0 || h == 0)) {
showMsgDialog(context, "Error", "Please add the cartion size"); showMsgDialog(context, "Error", "Please add the carton size");
return; return;
} }

View File

@@ -15,6 +15,8 @@ class CartonSelectionModel extends BaseModel {
bool reachEnd = false; bool reachEnd = false;
List<Carton> cartons = []; List<Carton> cartons = [];
bool isLoading = false;
// for default carton // for default carton
DocumentSnapshot? _lastDocument; DocumentSnapshot? _lastDocument;
bool ended = false; bool ended = false;
@@ -96,10 +98,11 @@ class CartonSelectionModel extends BaseModel {
int rowPerPage = 20; int rowPerPage = 20;
try { try {
isLoading = true;
String path = "/$cartons_collection"; String path = "/$cartons_collection";
Query query = FirebaseFirestore.instance Query query = FirebaseFirestore.instance
.collection(path) .collection(path)
.where("fcs_shipment_id", isEqualTo: shipmentId) // .where("fcs_shipment_id", isEqualTo: shipmentId)
// .where("status", isEqualTo: carton_processing_status) // .where("status", isEqualTo: carton_processing_status)
// .where("carton_type", isEqualTo: carton_mix_box) // .where("carton_type", isEqualTo: carton_mix_box)
.where("is_deleted", isEqualTo: false) .where("is_deleted", isEqualTo: false)
@@ -131,6 +134,8 @@ class CartonSelectionModel extends BaseModel {
notifyListeners(); notifyListeners();
} catch (e) { } catch (e) {
log.warning("error:$e"); log.warning("error:$e");
} finally {
isLoading = false;
} }
} }

View File

@@ -0,0 +1,170 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
import '../../../domain/constants.dart';
import '../../../domain/entities/package.dart';
import '../../main/model/base_model.dart';
class PackageSelectionModel extends BaseModel {
final log = Logger("PackageSelectionModel");
// for search
String query = "";
int offset = 0;
bool reachEnd = false;
List<Package> packages = [];
bool isLoading = false;
// for default package
DocumentSnapshot? _lastDocument;
bool ended = false;
List<Package> selectedPackageList = [];
Timer? t;
search(String term,
{bool imm = false,
required String shipmentId,
required String senderId,
required String consigneeId}) async {
query = term;
packages.clear();
offset = 0;
reachEnd = false;
t?.cancel();
t = Timer(Duration(milliseconds: imm ? 0 : 800), () async {
await loadMoreSearch(
term: term,
shipmentId: shipmentId,
consigneeId: consigneeId,
senderId: senderId);
});
}
Future<void> loadMoreSearch(
{required String term,
required String shipmentId,
required String senderId,
required String consigneeId}) async {
if (term == "") {
await _refresh(
shipmentId: shipmentId, senderId: senderId, consigneeId: consigneeId);
return;
}
// int rowPerPage = 21;
// List<Carton> list = [];
// SearchPara searchPara = SearchPara(filters: [], term: term);
// isLoading = true;
// var path =
// "/search/$cartons_collection/${searchPara.escapeTerm}/$rowPerPage/$offset/${searchPara.escapeFilters}";
// var result = await requestAPI(path, "GET",
// token: await getToken(), url: Config.instance.searchURL);
// if (result != null) {
// for (var row in result) {
// var item = ArtistExt.fromMapForSearch(row);
// list.add(item);
// }
// }
// for (var p in list) {
// selectedArtistList.contains(p)
// ? p.isSelected = true
// : p.isSelected = false;
// }
// artists.addAll(list);
// offset += rowPerPage;
// if (list.length < rowPerPage) {
// reachEnd = true;
// }
notifyListeners();
}
addDefaultPackages(
{required String shipmentId,
required String senderId,
required String consigneeId}) async {
packages.clear();
await _refresh(
shipmentId: shipmentId, senderId: senderId, consigneeId: consigneeId);
}
selectPackage(Package a) {
if (a.isChecked) {
selectedPackageList.add(a);
} else {
selectedPackageList.remove(a);
}
}
Future<void> _refresh(
{required String shipmentId,
required String senderId,
required String consigneeId}) async {
packages.clear();
_lastDocument = null;
ended = false;
await loadMoreData(
shipmentId: shipmentId, senderId: senderId, consigneeId: consigneeId);
notifyListeners();
}
Future<void> loadMoreData(
{required String shipmentId,
required String senderId,
required String consigneeId}) async {
int rowPerPage = 20;
try {
isLoading = true;
String path = "/$packages_collection";
Query query = FirebaseFirestore.instance
.collection(path)
// .where("fcs_shipment_id", isEqualTo: shipmentId)
// .where("status", isEqualTo: package_processed_status)
.where("user_id", whereIn: [senderId, consigneeId])
.where("is_deleted", isEqualTo: false)
.orderBy("created_date", descending: true);
if (_lastDocument != null) {
query = query.startAfterDocument(_lastDocument!);
}
QuerySnapshot querySnap = await query.limit(rowPerPage).get();
if (querySnap.docs.isEmpty) return;
_lastDocument = querySnap.docs[querySnap.docs.length - 1];
List<Package> list = querySnap.docs.map((documentSnapshot) {
var p = Package.fromMap(documentSnapshot.data() as Map<String, dynamic>,
documentSnapshot.id);
return p;
}).toList();
for (var p in list) {
selectedPackageList.contains(p)
? p.isChecked = true
: p.isChecked = false;
}
packages.addAll(list);
if (list.length < rowPerPage) ended = true;
notifyListeners();
} catch (e) {
log.warning("error:$e");
} finally {
isLoading = false;
}
}
clearSelection() {
selectedPackageList.clear();
packages.clear();
query = "";
}
}

View File

@@ -0,0 +1,120 @@
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import '../../../helpers/theme.dart';
import '../../domain/entities/package.dart';
import 'model/package_selection_model.dart';
typedef OnAction = Future<void> Function();
final NumberFormat numberFormatter = NumberFormat("#,###");
class PackageSelectionResult extends StatelessWidget {
final bool isLoadingMore;
final OnAction onLoadMore;
final OnAction onRefresh;
final Function(Package)? onTap;
final ScrollController controller;
const PackageSelectionResult(
{super.key,
required this.isLoadingMore,
required this.onLoadMore,
required this.onRefresh,
this.onTap,
required this.controller});
bool _scrollNotification(ScrollNotification scrollInfo) {
if (!isLoadingMore &&
scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
onLoadMore();
}
return true;
}
@override
Widget build(BuildContext context) {
var model = context.watch<PackageSelectionModel>();
List<Package> searchResults = model.packages;
return searchResults.isEmpty && !model.isLoading
? Center(
child: LocalText(context, 'box.no_package',
color: Colors.black, fontSize: 15))
: Column(children: [
Expanded(
child: NotificationListener<ScrollNotification>(
onNotification: _scrollNotification,
child: RefreshIndicator(
color: primaryColor,
onRefresh: () => onRefresh(),
child: ListView.builder(
controller: controller,
shrinkWrap: true,
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (context, index) {
Package package = searchResults[index];
return Padding(
padding: const EdgeInsets.only(top: 5, bottom: 5),
child: InkWell(
onTap: () {
if (onTap != null) {
onTap!(package);
}
},
child: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(5)),
border:
Border.all(color: Colors.grey.shade300)),
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
children: <Widget>[
Expanded(
child: new Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0),
child: new Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
new Text(package.trackingID ?? "",
style: new TextStyle(
fontSize: 15.0,
color: Colors.black)),
new Text(
package.cartonIds.isEmpty
? "-"
: "${numberFormatter.format(package.cartonIds.length)} Boxes",
style: new TextStyle(
fontSize: 15.0,
color: Colors.grey),
),
],
),
),
),
package.isChecked
? Icon(Icons.check, color: primaryColor)
: const SizedBox()
],
),
),
),
);
},
itemCount: searchResults.length)),
)),
Container(
height: isLoadingMore ? 50.0 : 0,
color: Colors.transparent,
child: const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(primaryColor)),
)),
]);
}
}

View File

@@ -0,0 +1,289 @@
import 'package:fcs/domain/entities/fcs_shipment.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart';
import 'package:provider/provider.dart';
import '../../domain/entities/package.dart';
import '../../domain/entities/user.dart';
import '../main/util.dart';
import '../widgets/barcode_scanner.dart';
import '../widgets/continue_button.dart';
import '../widgets/display_text.dart';
import '../widgets/local_title.dart';
import '../widgets/previous_button.dart';
import 'model/carton_selection_model.dart';
import 'model/package_selection_model.dart';
import 'package_selection_result.dart';
typedef OnPrevious = Function(List<Package> packages);
typedef OnContinue = Function(List<Package> packages);
class PackageSelectionWidget extends StatefulWidget {
final User sender;
final User consignee;
final FcsShipment shipment;
final List<Package> packages;
final OnPrevious? onPrevious;
final OnContinue? onContinue;
const PackageSelectionWidget({
Key? key,
required this.packages,
this.onPrevious,
this.onContinue,
required this.shipment,
required this.sender,
required this.consignee,
}) : super(key: key);
@override
State<PackageSelectionWidget> createState() => _PackageSelectionWidgetState();
}
class _PackageSelectionWidgetState extends State<PackageSelectionWidget> {
final TextEditingController _controller = TextEditingController();
String _query = "";
bool _isLoadMore = false;
final _scrollController = ScrollController();
@override
void initState() {
_init();
super.initState();
}
_init() {
var searchModel = context.read<PackageSelectionModel>();
searchModel.addDefaultPackages(
shipmentId: widget.shipment.id!,
consigneeId: widget.consignee.id!,
senderId: widget.sender.id!);
_controller.text = searchModel.query;
_query = searchModel.query;
if (mounted) {
setState(() {});
}
}
@override
void didUpdateWidget(covariant PackageSelectionWidget oldWidget) {
_init();
super.didUpdateWidget(oldWidget);
}
Future<void> _loadMoreData() async {
if (_isLoadMore) return;
var model = context.read<PackageSelectionModel>();
if (model.reachEnd || model.ended) return;
setState(() {
_isLoadMore = true;
});
if (_query != "") {
await model.loadMoreSearch(
term: _query,
shipmentId: widget.shipment.id!,
consigneeId: widget.consignee.id!,
senderId: widget.sender.id!);
} else {
await model.loadMoreData(
shipmentId: widget.shipment.id!,
consigneeId: widget.consignee.id!,
senderId: widget.sender.id!);
}
setState(() {
_isLoadMore = false;
});
}
@override
Widget build(BuildContext context) {
var model = context.watch<PackageSelectionModel>();
List<Package> searchResults = model.packages;
List<Package> selectedPackageList = model.selectedPackageList;
final senderBox = DisplayText(
text: widget.sender.name,
labelTextKey: "box.sender.title",
iconData: MaterialCommunityIcons.account_arrow_right,
subText: Text(widget.sender.fcsID!,
style: TextStyle(fontSize: 13, color: labelColor)),
);
final consigneeBox = DisplayText(
text: widget.consignee.name,
labelTextKey: "box.consignee.title",
iconData: MaterialCommunityIcons.account_arrow_left,
subText: Text(widget.consignee.fcsID!,
style: TextStyle(fontSize: 13, color: labelColor)),
);
final userRow = Row(
children: [
Expanded(
child: senderBox,
flex: 2,
),
Flexible(child: consigneeBox)
],
);
final continueBtn = ContinueButton(
onTap: () {
if (selectedPackageList.isEmpty || searchResults.isEmpty) {
showMsgDialog(context, 'Error', "Please select the packages");
return false;
}
if (widget.onContinue != null) {
widget.onContinue!(selectedPackageList);
}
},
);
final previousBtn = PreviousButton(onTap: () {
if (widget.onPrevious != null) {
widget.onPrevious!(selectedPackageList);
}
});
final searchBox = SizedBox(
height: 40,
child: TextField(
controller: _controller,
cursorColor: primaryColor,
onSubmitted: (value) {
setState(() {
_query = value;
});
_search(imm: true);
},
onChanged: (v) {
setState(() {
_query = v;
});
_search();
},
decoration: InputDecoration(
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: labelColor),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
borderSide:
BorderSide(color: labelColor.withOpacity(0.3), width: 0)),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
borderSide: BorderSide(color: labelColor),
),
hintText: "Search by tracking number",
hintStyle: const TextStyle(
color: Colors.grey,
fontSize: 16,
fontWeight: FontWeight.normal),
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
children: [
InkResponse(
radius: 20,
onTap: () {
_scan(context);
},
child: const Icon(Icons.qr_code_scanner,
color: Colors.black87)),
IconButton(
splashRadius: 20,
onPressed: () {
setState(() {
_controller.clear();
_query = "";
});
_search();
},
icon: const Icon(Icons.close, color: Colors.black87)),
],
),
contentPadding: const EdgeInsets.all(10),
filled: true),
),
);
return Column(
children: [
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 10, right: 10),
child: Column(
children: [
const SizedBox(height: 8),
userRow,
LocalTitle(textKey: "box.select.package", topPadding: 10),
const SizedBox(height: 10),
searchBox,
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 10),
child: PackageSelectionResult(
controller: _scrollController,
isLoadingMore: _isLoadMore,
onLoadMore: _loadMoreData,
onRefresh: () async {
_init();
},
onTap: (a) async {
setState(() {
a.isChecked = !a.isChecked;
});
context.read<PackageSelectionModel>().selectPackage(a);
},
),
),
),
],
),
),
),
widget.onContinue != null
? Padding(
padding: const EdgeInsets.only(left: 15, right: 15, top: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
previousBtn,
continueBtn,
],
),
)
: const SizedBox(),
const SizedBox(height: 20)
],
);
}
_scan(BuildContext context) async {
try {
String? barcode = await scanBarcode();
if (barcode != null) {
setState(() {
_controller.text = barcode;
_query = barcode;
});
await _search();
}
} catch (e) {
showMsgDialog(context, 'Error', e.toString());
}
}
_search({bool imm = false}) async {
try {
await context
.read<CartonSelectionModel>()
.search(_query, imm: imm, shipmentId: widget.shipment.id!);
} catch (e) {
showMsgDialog(context, 'Error', e.toString());
}
}
}

View File

@@ -1,7 +1,6 @@
import 'package:fcs/helpers/theme.dart'; import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/fcs_shipment/model/fcs_shipment_model.dart'; import 'package:fcs/pages/fcs_shipment/model/fcs_shipment_model.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart'; import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/local_popup_menu_button.dart';
import 'package:fcs/pages/widgets/local_popupmenu.dart'; import 'package:fcs/pages/widgets/local_popupmenu.dart';
import 'package:fcs/pages/widgets/local_text.dart'; import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart'; import 'package:fcs/pages/widgets/progress.dart';

View File

@@ -6,7 +6,7 @@ import 'package:provider/provider.dart';
class DisplayText extends StatelessWidget { class DisplayText extends StatelessWidget {
final String? text; final String? text;
final String? text1; final Widget? subText;
final String? labelTextKey; final String? labelTextKey;
final IconData? iconData; final IconData? iconData;
final int? maxLines; final int? maxLines;
@@ -14,11 +14,10 @@ class DisplayText extends StatelessWidget {
final Color? borderColor; final Color? borderColor;
final Widget? icon; final Widget? icon;
const DisplayText({ const DisplayText({
Key? key, Key? key,
this.text, this.text,
this.text1, this.subText,
this.labelTextKey, this.labelTextKey,
this.iconData, this.iconData,
this.maxLines = 1, this.maxLines = 1,
@@ -72,12 +71,9 @@ class DisplayText extends StatelessWidget {
text!, text!,
style: textStyle, style: textStyle,
), ),
text1 == null subText == null
? Container() ? Container()
: Text( : subText!,
text1!,
style: textStyle,
),
], ],
), ),
), ),

View File

@@ -5,10 +5,11 @@ import 'package:flutter/material.dart';
class LocalTitle extends StatelessWidget { class LocalTitle extends StatelessWidget {
final String? textKey; final String? textKey;
final Widget? trailing; final Widget? trailing;
final double topPadding;
final List<String>? translationVariables; final List<String>? translationVariables;
const LocalTitle( const LocalTitle(
{Key? key, this.textKey, this.trailing, this.translationVariables}) {Key? key, this.textKey, this.trailing, this.translationVariables,this.topPadding = 18})
: super(key: key); : super(key: key);
@override @override
@@ -17,7 +18,7 @@ class LocalTitle extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( Container(
padding: EdgeInsets.only(top: 18), padding: EdgeInsets.only(top: topPadding),
child: Row( child: Row(
children: [ children: [
LocalText(context, textKey!, LocalText(context, textKey!,

View File

@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import '../../helpers/theme.dart';
import 'local_text.dart';
class SubmitTextWidget extends StatelessWidget {
final String labelKey;
final String text;
final String? subText;
const SubmitTextWidget(
{Key? key, required this.labelKey, required this.text, this.subText})
: super(key: key);
@override
Widget build(BuildContext context) {
return Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Padding(
padding: const EdgeInsets.only(left: 5, bottom: 5),
child: LocalText(context, labelKey,
color: primaryColor, fontSize: 16, fontWeight: FontWeight.normal),
),
Container(
decoration: BoxDecoration(
border: Border.all(color: primaryColor),
borderRadius: BorderRadius.circular(5),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(text,
style: const TextStyle(
fontSize: 16.0, fontWeight: FontWeight.w500)),
subText == null
? const SizedBox()
: Text(subText!,
style:
const TextStyle(fontSize: 14.0, color: Colors.grey)),
],
),
),
)
]);
}
}