update package detal list and update cargo type input in carton section

This commit is contained in:
tzw
2025-03-25 17:38:51 +06:30
parent 3d4bc43de4
commit 2bd75f0333
14 changed files with 597 additions and 812 deletions

View File

@@ -1,3 +1,5 @@
import 'package:fcs/pages/main/util.dart';
class CargoType {
String? id;
String? name;
@@ -96,7 +98,7 @@ class CargoType {
Map<String, dynamic> toMapForCarton() {
return {
"id": id,
'weight': weight,
'weight': double.tryParse(twoDecimalFormatted(weight)) ?? 0.00,
"mix_cargo_type_ids": mixCargoes.map((e) => e.id).toList()
};
}
@@ -109,6 +111,22 @@ class CargoType {
return CargoType.fromMap(toMap(), id!);
}
CargoType cloneForCarton() {
return CargoType(
id: id,
name: name,
weight: weight,
isDefault: isDefault,
displayIndex: displayIndex,
isMixCargo: isMixCargo,
mixCargoIds: List.from(mixCargoIds),
mixCargoes: List.from(mixCargoes));
}
CargoType cloneForSurchage() {
return CargoType(id: id, name: name, qty: qty);
}
@override
bool operator ==(Object other) => other is CargoType && other.id == id;

View File

@@ -52,9 +52,9 @@ class _CargoWidgetState extends State<CargoWidget> {
List<TextEditingController> surchargeControllers = [];
bool get hasValueTotalWeight =>
totalCtl.text.isNotEmpty && totalCtl.text != '0';
totalCtl.text.isNotEmpty && totalCtl.text != '0.00';
bool get hasValueCargoes =>
_cargoTypes.isNotEmpty && _cargoTypes.every((e) => e.weight != 0);
_cargoTypes.isNotEmpty && _cargoTypes.every((e) => e.weight != 0.00);
double get actualTotalWeight =>
_cargoTypes.fold(0, (sum, value) => sum + value.weight);
@@ -72,11 +72,12 @@ class _CargoWidgetState extends State<CargoWidget> {
_cargoTypes = List.from(widget.cargoTypes);
for (var e in _cargoTypes) {
var editor = TextEditingController();
editor.text = removeTrailingZeros(e.weight);
editor.text = twoDecimalFormatted(
double.tryParse(removeTrailingZeros(e.weight)) ?? 0);
editor.addListener(inputChangeListener);
cargoTypeControllers.add(editor);
}
onUpdated();
_onPopulate();
} else {
WidgetsBinding.instance.addPostFrameCallback((_) {
_openCargoTypeSelection();
@@ -108,11 +109,14 @@ class _CargoWidgetState extends State<CargoWidget> {
for (var e in _cargoTypes) {
var editor = TextEditingController();
editor.text = '0';
editor.text = '0.00';
editor.addListener(inputChangeListener);
cargoTypeControllers.add(editor);
}
totalCtl.text = twoDecimalFormatted(
double.tryParse(removeTrailingZeros(actualTotalWeight)) ?? 0);
if (mounted) {
setState(() {});
}
@@ -133,9 +137,10 @@ class _CargoWidgetState extends State<CargoWidget> {
return emptyFields;
}
void onUpdated() {
void _onPopulate() {
if (!hasValueTotalWeight && hasValueCargoes) {
totalCtl.text = removeTrailingZeros(actualTotalWeight);
totalCtl.text = twoDecimalFormatted(
double.tryParse(removeTrailingZeros(actualTotalWeight)) ?? 0);
error = null;
} else {
// auto populate remaining value
@@ -155,8 +160,8 @@ class _CargoWidgetState extends State<CargoWidget> {
_cargoTypes.asMap().entries.forEach((e) {
if (e.value.weight == 0) {
e.value.weight = remainingWeight;
cargoTypeControllers[e.key].text =
removeTrailingZeros(e.value.weight);
cargoTypeControllers[e.key].text = twoDecimalFormatted(
double.tryParse(removeTrailingZeros(e.value.weight)) ?? 0);
}
});
}
@@ -168,6 +173,18 @@ class _CargoWidgetState extends State<CargoWidget> {
}
}
void _onCheckTotalWeight(String value) {
double totalWeight = double.tryParse(value) ?? 0;
if (totalWeight != actualTotalWeight) {
error = "Invalid total weight";
} else {
error = null;
}
if (mounted) {
setState(() {});
}
}
@override
Widget build(BuildContext context) {
final senderBox = userDisplayBox(context,
@@ -208,19 +225,30 @@ class _CargoWidgetState extends State<CargoWidget> {
CargoTypeAddition(cargoTypes: _cargoTypes)));
if (cargoType == null) return;
if (!cargoType.isMixCargo) {
_cargoTypes.add(cargoType);
}
_cargoTypes.sort((a, b) => (a == b ? 0 : (a.isMixCargo ? 1 : -1)));
// add cargo type
if (cargoType.isMixCargo) {
_cargoTypes.add(cargoType);
int lastTrueIndex =
_cargoTypes.lastIndexWhere((e) => e.isMixCargo);
if (lastTrueIndex != -1) {
_cargoTypes.insert(lastTrueIndex + 1, cargoType);
} else {
_cargoTypes.add(cargoType);
}
} else {
int lastFalseIndex =
_cargoTypes.lastIndexWhere((e) => !e.isMixCargo);
if (lastFalseIndex != -1) {
_cargoTypes.insert(lastFalseIndex + 1, cargoType);
} else {
_cargoTypes.insert(0, cargoType);
}
}
cargoTypeControllers.clear();
for (var e in _cargoTypes) {
var editor = TextEditingController();
editor.text = removeTrailingZeros(e.weight);
editor.text = twoDecimalFormatted(
double.tryParse(removeTrailingZeros(e.weight)) ?? 0);
editor.addListener(inputChangeListener);
cargoTypeControllers.add(editor);
}
@@ -230,177 +258,53 @@ class _CargoWidgetState extends State<CargoWidget> {
}),
);
final cargosBox = Padding(
final totalWeightBox = Padding(
padding: const EdgeInsets.only(top: 5),
child: Wrap(
alignment: WrapAlignment.spaceBetween,
runSpacing: 25,
children: _cargoTypes.asMap().entries.map((e) {
var key = e.key;
var c = e.value;
return SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
width: MediaQuery.of(context).size.width / 2.3,
child: Column(
child: Row(
children: [
Row(
children: [
InkResponse(
radius: 25,
onTap: () {
double totalWeight =
double.tryParse(totalCtl.text) ?? 0;
if (actualTotalWeight > totalWeight) {
error = "Exceed total weight";
} else {
double result = totalWeight - c.weight;
totalCtl.text = removeTrailingZeros(result);
}
_cargoTypes.removeAt(key);
cargoTypeControllers.removeAt(key);
if (mounted) {
setState(() {});
}
},
child: Icon(Feather.minus_circle, color: labelColor)),
const SizedBox(width: 10),
Flexible(
child: inputTextFieldWidget(context,
lableText: c.name ?? "",
controller: cargoTypeControllers[key],
onFieldSubmitted: (value) {
_cargoTypes[e.key].weight =
double.tryParse(value) ?? 0;
onUpdated();
},
suffixIcon: InkResponse(
radius: 23,
onTap: () {
double totalWeight =
(double.tryParse(totalCtl.text) ?? 0);
var list = _cargoTypes
.where((e) => e.id != c.id)
.toList();
double resetValue = totalWeight -
(list.fold(0,
(sum, value) => sum + value.weight));
setState(() {
e.value.weight = resetValue;
cargoTypeControllers[e.key].text =
removeTrailingZeros(resetValue);
});
onUpdated();
},
child: Icon(Ionicons.md_refresh_circle,
color: labelColor, size: 22))),
),
c.isMixCargo
? InkResponse(
radius: 23,
onTap: () async {
List<CargoType>? cargoes = await showDialog(
context: context,
builder: (_) => MixCargoTypeAdditionDialog(
cargoTypes: c.mixCargoes));
if (cargoes == null) return;
setState(() {
c.mixCargoes = List.from(cargoes);
});
},
child: Icon(Icons.add_circle,
color: labelColor, size: 22))
: const SizedBox()
],
),
c.mixCargoes.isEmpty
? const SizedBox()
: Padding(
padding: const EdgeInsets.only(top: 5),
child: Column(
children: c.mixCargoes.map((e) {
return Padding(
padding: const EdgeInsets.only(top: 12),
child: Row(
children: [
const SizedBox(width: 25),
InkResponse(
radius: 23,
onTap: () {
setState(() {
c.mixCargoes.remove(e);
});
},
child: Icon(Feather.minus_circle,
color: labelColor, size: 20)),
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(e.name ?? ""),
),
],
),
);
}).toList()),
)
],
),
);
}).toList()),
);
final totalWeightBox = Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
width: MediaQuery.of(context).size.width / 2.3,
child: Row(
children: [
InkResponse(
radius: 25,
onTap: () {
setState(() {
totalCtl.clear();
});
},
child: Icon(MaterialIcons.clear, color: labelColor)),
const SizedBox(width: 10),
Flexible(
child: inputTextFieldWidget(context,
lableText: "Total",
controller: totalCtl, onFieldSubmitted: (neValue) {
if (hasValueCargoes) {
double totalWeight = double.tryParse(neValue) ?? 0;
if (totalWeight < actualTotalWeight) {
InkResponse(
radius: 25,
onTap: () {
setState(() {
error = "Invalid total weight";
totalCtl.clear();
});
_onCheckTotalWeight(totalCtl.text);
},
child: Icon(MaterialIcons.clear, color: labelColor)),
const SizedBox(width: 10),
Flexible(
child: inputTextFieldWidget(context,
lableText: "Total",
controller: totalCtl, onFieldSubmitted: (newValue) {
if (hasValueCargoes) {
_onCheckTotalWeight(newValue);
} else {
setState(() {
error = null;
});
_onPopulate();
}
} else {
onUpdated();
}
},
suffixIcon: InkResponse(
radius: 23,
onTap: () {
setState(() {
totalCtl.text =
removeTrailingZeros(actualTotalWeight);
error = null;
});
},
child: Icon(Ionicons.md_refresh_circle,
color: labelColor, size: 22))),
),
],
)),
],
},
suffixIcon: InkResponse(
radius: 23,
onTap: () {
setState(() {
totalCtl.text = twoDecimalFormatted(
double.tryParse(removeTrailingZeros(
actualTotalWeight)) ??
0);
error = null;
});
},
child: Icon(Ionicons.md_refresh_circle,
color: labelColor, size: 22))),
),
],
)),
],
),
);
final subchargeItemTitleBox = LocalTitle(
@@ -481,17 +385,161 @@ class _CargoWidgetState extends State<CargoWidget> {
context, "Error", "Please insert surcharge item quantity");
return;
}
if (error != null) {
showMsgDialog(
context, "Error", "Please add the right cargo type weight");
return;
}
widget.onContinue!(_cargoTypes, _surchareItems);
}
},
);
final previousBtn = PreviousButton(onTap: () {
if (error != null) {
showMsgDialog(
context, "Error", "Please add the right cargo type weight");
return;
}
if (widget.onPrevious != null) {
widget.onPrevious!(_cargoTypes, _surchareItems);
}
});
Widget cargoesWidget(List<CargoType> items) {
List<Widget> widgets = [];
for (int i = 0; i < items.length; i++) {
var key = i;
var c = items[i];
if (i > 0 && (!items[i - 1].isMixCargo && items[i].isMixCargo)) {
widgets.add(Padding(
padding: const EdgeInsets.symmetric(horizontal: 70),
child: Divider(color: Colors.grey.shade300)));
}
widgets.add(SizedBox(
width: MediaQuery.of(context).size.width / 2.3,
child: Column(
children: [
Row(
children: [
InkResponse(
radius: 25,
onTap: () {
_cargoTypes.removeAt(key);
cargoTypeControllers.removeAt(key);
_onCheckTotalWeight(totalCtl.text);
if (mounted) {
setState(() {});
}
},
child: Icon(Feather.minus_circle, color: labelColor)),
const SizedBox(width: 10),
Flexible(
child: inputTextFieldWidget(context,
lableText: c.name ?? "",
controller: cargoTypeControllers[key],
onFieldSubmitted: (value) {
_cargoTypes[key].weight = double.tryParse(value) ?? 0;
_onPopulate();
},
suffixIcon: InkResponse(
radius: 23,
onTap: () {
double totalWeight =
(double.tryParse(totalCtl.text) ?? 0);
var list = _cargoTypes
.where((e) => e.id != c.id)
.toList();
double sum = (list.fold(
0, (sum, value) => sum + value.weight));
if (sum > totalWeight) {
error = "Exceed total weight";
} else {
error = null;
double resetValue = totalWeight - sum;
setState(() {
c.weight = resetValue;
cargoTypeControllers[key].text =
twoDecimalFormatted(double.tryParse(
removeTrailingZeros(
resetValue)) ??
0);
});
_onPopulate();
}
},
child: Icon(Ionicons.md_refresh_circle,
color: labelColor, size: 22))),
),
c.isMixCargo
? InkResponse(
radius: 23,
onTap: () async {
List<CargoType>? cargoes = await showDialog(
context: context,
builder: (_) => MixCargoTypeAdditionDialog(
cargoTypes: c.mixCargoes));
if (cargoes == null) return;
setState(() {
c.mixCargoes = List.from(cargoes);
});
},
child: Icon(Icons.add_circle,
color: labelColor, size: 22))
: const SizedBox()
],
),
c.mixCargoes.isEmpty
? const SizedBox()
: Padding(
padding: const EdgeInsets.only(top: 5),
child: Column(
children: c.mixCargoes.map((e) {
return Padding(
padding: const EdgeInsets.only(top: 12),
child: Row(
children: [
const SizedBox(width: 25),
InkResponse(
radius: 23,
onTap: () {
setState(() {
c.mixCargoes.remove(e);
});
},
child: Icon(Feather.minus_circle,
color: labelColor, size: 20)),
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(e.name ?? ""),
),
],
),
);
}).toList()),
)
],
),
));
}
return Padding(
padding: const EdgeInsets.only(top: 5),
child: Wrap(
alignment: WrapAlignment.spaceBetween,
runSpacing: 25,
children: widgets),
);
}
return Column(
children: [
Expanded(
@@ -502,9 +550,13 @@ class _CargoWidgetState extends State<CargoWidget> {
const SizedBox(height: 8),
userRow,
cargoTitle,
cargosBox,
cargoesWidget(_cargoTypes),
const SizedBox(height: 15),
Divider(),
_cargoTypes.isNotEmpty
? Divider(
color: Colors.grey.shade300,
)
: const SizedBox(),
const SizedBox(height: 5),
error != null
? Text(
@@ -553,7 +605,11 @@ class _CargoWidgetState extends State<CargoWidget> {
onFieldSubmitted: onFieldSubmitted,
readOnly: readOnly,
decoration: InputDecoration(
suffixIcon: suffixIcon,
suffixIcon: Padding(
padding: const EdgeInsets.only(right: 8),
child: suffixIcon,
),
suffixIconConstraints: BoxConstraints(minWidth: 0, minHeight: 0),
contentPadding: EdgeInsets.all(0),
labelText: lableText,
labelStyle: newLabelStyle(color: Colors.black54, fontSize: 17),

View File

@@ -7,7 +7,6 @@ import 'package:fcs/domain/entities/package.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/carton/carton_image_upload_editor.dart';
import 'package:fcs/pages/main/util.dart';
import 'package:fcs/pages/package/model/package_model.dart';
import 'package:fcs/pages/widgets/display_text.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/local_text.dart';
@@ -28,6 +27,7 @@ import 'carton_package_editor.dart';
import 'mix_carton/mix_carton_editor.dart';
import 'mix_carton_detail_list.dart';
import 'model/carton_model.dart';
import 'model/package_selection_model.dart';
import 'package_detail_list.dart';
import 'print_qr_code_page.dart';
@@ -48,10 +48,11 @@ class _CartonInfoState extends State<CartonInfo> {
List<CargoType> _cargoTypes = [];
List<CargoType> _surchareItems = [];
List<Carton> _mixCartons = [];
List<Package> _packages = [];
double totalWeight = 0.0;
double totalSurchargeCount = 0.0;
CartonSize? standardSize;
double _totalWeight = 0.0;
int _packageCount = 0;
CartonSize? _standardSize;
FcsShipment? _shipment;
@override
@@ -77,7 +78,7 @@ class _CartonInfoState extends State<CartonInfo> {
bool isStandartSize = sameLength && sameWidth && sameHeight;
if (isStandartSize) {
_carton.cartonSizeType = standardCarton;
standardSize = cartonSizes.firstWhere((size) =>
_standardSize = cartonSizes.firstWhere((size) =>
size.length == _carton.length &&
size.width == _carton.width &&
size.height == _carton.height);
@@ -89,32 +90,11 @@ class _CartonInfoState extends State<CartonInfo> {
_carton.cartonSizeType = customCarton;
}
if (widget.carton.fcsShipmentID != null &&
widget.carton.fcsShipmentID != '') {
var s = await context
.read<FcsShipmentModel>()
.getFcsShipment(widget.carton.fcsShipmentID!);
_shipment = s;
}
await Future.wait(
[_loadFcsShipment(), _loadPackageCount(), _loadMixCargoes()]);
if (_carton.cartonType == carton_from_packages) {
_packages = await context
.read<PackageModel>()
.getPackagesByIds(_carton.packageIDs);
}
if (_carton.cartonType == mix_carton) {
_mixCartons = await context
.read<CartonModel>()
.getCartonsByIds(_carton.cartonIDs);
_cargoTypes.sort((a, b) => a.name!.compareTo(b.name!));
_surchareItems.sort((a, b) => a.name!.compareTo(b.name!));
}
totalWeight =
_totalWeight =
_carton.cargoTypes.fold(0, (sum, value) => sum + value.weight);
totalSurchargeCount =
_surchareItems.fold(0, (sum, value) => sum + value.qty);
} finally {
_isLoading = false;
}
@@ -124,12 +104,50 @@ class _CartonInfoState extends State<CartonInfo> {
}
}
Future<void> _loadFcsShipment() async {
if (widget.carton.fcsShipmentID != null &&
widget.carton.fcsShipmentID != '') {
var s = await context
.read<FcsShipmentModel>()
.getFcsShipment(widget.carton.fcsShipmentID!);
_shipment = s;
if (mounted) {
setState(() {});
}
}
}
Future<void> _loadPackageCount() async {
if (_carton.cartonType == carton_from_packages) {
int? count = await context.read<PackageSelectionModel>().getPackageCount(
senderId: widget.carton.senderID ?? "",
consigneeId: widget.carton.consigneeID ?? "",
shipmentId: widget.carton.fcsShipmentID ?? "");
_packageCount = count ?? 0;
if (mounted) {
setState(() {});
}
}
}
Future<void> _loadMixCargoes() async {
if (_carton.cartonType == mix_carton) {
_mixCartons =
await context.read<CartonModel>().getCartonsByIds(_carton.cartonIDs);
_cargoTypes.sort((a, b) => a.name!.compareTo(b.name!));
_surchareItems.sort((a, b) => a.name!.compareTo(b.name!));
if (mounted) {
setState(() {});
}
}
}
@override
Widget build(BuildContext context) {
var fromPackage = _carton.cartonType == carton_from_packages;
String? boxDimension = _carton.cartonSizeType == standardCarton
? "${standardSize?.name} - ${standardSize?.length.toInt()}”x${standardSize?.width.toInt()}”x${standardSize?.height.toInt()}"
? "${_standardSize?.name} - ${_standardSize?.length.toInt()}”x${_standardSize?.width.toInt()}”x${_standardSize?.height.toInt()}"
: _carton.cartonSizeType == customCarton
? "${_carton.length.toInt()}”x${_carton.width.toInt()}”x${_carton.height.toInt()}"
: null;
@@ -178,15 +196,19 @@ class _CartonInfoState extends State<CartonInfo> {
final packageLengthBox = DisplayText(
showLabelLink: true,
subText: Text(numberFormatter.format(_packages.length)),
subText: Text(numberFormatter.format(_packageCount)),
labelTextKey: "box.package",
onTapLabel: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => PackageDetailList(
cartonNumber: _carton.cartonNumber ?? '',
packages: _packages)));
cartonNumber: _carton.cartonNumber ?? '',
packageCount: _packageCount,
senderID: _carton.senderID ?? "",
consingeeID: _carton.consigneeID ?? "",
fcsShipmentID: _carton.fcsShipmentID ?? "",
)));
},
);
@@ -278,7 +300,8 @@ class _CartonInfoState extends State<CartonInfo> {
_cargoTypes.isNotEmpty
? Padding(
padding: EdgeInsets.only(right: 0),
child: Text("${removeTrailingZeros(totalWeight)} lb",
child: Text(
"${twoDecimalFormatted(double.tryParse(removeTrailingZeros(_totalWeight)) ?? 0)} lb",
textAlign: TextAlign.end,
style: TextStyle(color: Colors.black54, fontSize: 15)))
: const SizedBox()
@@ -307,7 +330,7 @@ class _CartonInfoState extends State<CartonInfo> {
color: Colors.black, fontSize: 15),
),
Text(
"${removeTrailingZeros(e.value.weight)} lb",
"${twoDecimalFormatted(double.tryParse(removeTrailingZeros(e.value.weight)) ?? 0)} lb",
textAlign: TextAlign.end,
style: TextStyle(
color: Colors.black, fontSize: 15))
@@ -528,7 +551,7 @@ class _CartonInfoState extends State<CartonInfo> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: cartonSizeBox),
_packages.isEmpty
_packageCount == 0
? const SizedBox()
: Flexible(child: packageLengthBox),
],

View File

@@ -13,12 +13,10 @@ import '../../../domain/vo/local_step.dart';
import '../../../helpers/theme.dart';
import '../../domain/entities/cargo_type.dart';
import '../../domain/entities/carton.dart';
import '../../domain/entities/package.dart';
import '../../domain/entities/user.dart';
import '../carton_size/model/carton_size_model.dart';
import '../fcs_shipment/model/fcs_shipment_model.dart';
import '../main/util.dart';
import '../package/model/package_model.dart';
import '../widgets/local_text.dart';
import '../widgets/progress.dart';
import '../widgets/step_widget.dart';
@@ -26,7 +24,7 @@ import 'cargo_widget.dart';
import 'carton_size_widget.dart';
import 'carton_submit.dart';
import 'model/package_selection_model.dart';
import 'packages_widget.dart';
import 'package_selection_widget.dart';
class CartonPackageEditor extends StatefulWidget {
final Carton carton;
@@ -46,7 +44,7 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
LocalStep(lable: 'Cargos', stepType: StepType.CARGOS),
LocalStep(lable: 'Submit', stepType: StepType.SUBMIT)
];
List<Package> _packages = [];
List<CargoType> _cargoTypes = [];
List<CargoType> _surchareItems = [];
@@ -86,8 +84,11 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
_billToValue = widget.carton.billTo ?? billToSender;
_selectedLastMile = widget.carton.lastMile ?? delivery_caton;
_cargoTypes = widget.carton.cargoTypes;
_surchareItems = widget.carton.surchareItems;
_cargoTypes =
widget.carton.cargoTypes.map((e) => e.cloneForCarton()).toList();
_surchareItems =
widget.carton.surchareItems.map((e) => e.cloneForSurchage()).toList();
// check carton size type
List<CartonSize> cartonSizes = context.read<CartonSizeModel>().cartonSizes;
@@ -120,11 +121,6 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
.read<FcsShipmentModel>()
.getFcsShipment(widget.carton.fcsShipmentID ?? "");
_shipment = s;
_packages = await context
.read<PackageModel>()
.getPackagesByIds(widget.carton.packageIDs);
if (mounted) {
setState(() {});
}
@@ -233,19 +229,17 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
));
} else if (step.stepType == StepType.PACKAGES) {
return Expanded(
child: PackagesWidget(
child: PackageSelectionWidget(
sender: _sender!,
consignee: _consignee!,
shipment: _shipment!,
onContinue: (packages) {
onContinue: () {
setState(() {
_packages = List.from(packages);
currentStep += 1;
});
},
onPrevious: (packages) {
onPrevious: () {
setState(() {
_packages = List.from(packages);
currentStep -= 1;
});
},
@@ -288,7 +282,7 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
height: _height,
lastMile: _selectedLastMile,
shipment: _shipment!,
packages: _packages,
// packages: _packages,
cargoTypes: _cargoTypes,
surchareItems: _surchareItems,
onCreate: () {
@@ -340,7 +334,7 @@ class _CartonPackageEditorState extends State<CartonPackageEditor> {
length: length,
width: width,
height: height,
packages: _packages,
// packages: _packages,
cargoTypes: _cargoTypes,
surchareItems: _surchareItems);

View File

@@ -12,7 +12,6 @@ import '../../../domain/entities/fcs_shipment.dart';
import '../../../domain/vo/local_step.dart';
import '../../../helpers/theme.dart';
import '../../domain/entities/cargo_type.dart';
import '../../domain/entities/package.dart';
import '../../domain/entities/user.dart';
import '../main/util.dart';
import '../widgets/local_text.dart';
@@ -23,7 +22,7 @@ import 'carton_size_widget.dart';
import 'carton_submit.dart';
import 'model/carton_model.dart';
import 'model/package_selection_model.dart';
import 'packages_widget.dart';
import 'package_selection_widget.dart';
class CartonPackageForm extends StatefulWidget {
final User sender;
@@ -48,7 +47,7 @@ class _CartonPackageFormState extends State<CartonPackageForm> {
LocalStep(lable: 'Cargos', stepType: StepType.CARGOS),
LocalStep(lable: 'Submit', stepType: StepType.SUBMIT)
];
List<Package> _packages = [];
List<CargoType> _cargoTypes = [];
List<CargoType> _surchareItems = [];
@@ -166,19 +165,17 @@ class _CartonPackageFormState extends State<CartonPackageForm> {
));
} else if (step.stepType == StepType.PACKAGES) {
return Expanded(
child: PackagesWidget(
child: PackageSelectionWidget(
sender: widget.sender,
consignee: widget.consignee,
shipment: _shipment!,
onContinue: (packages) {
onContinue: () {
setState(() {
_packages = List.from(packages);
currentStep += 1;
});
},
onPrevious: (packages) {
onPrevious: () {
setState(() {
_packages = List.from(packages);
currentStep -= 1;
});
},
@@ -220,7 +217,6 @@ class _CartonPackageFormState extends State<CartonPackageForm> {
height: _height,
lastMile: _selectedLastMile,
shipment: _shipment!,
packages: _packages,
cargoTypes: _cargoTypes,
surchareItems: _surchareItems,
onCreate: () {
@@ -236,7 +232,6 @@ class _CartonPackageFormState extends State<CartonPackageForm> {
}
}
_create() async {
setState(() {
_isLoading = true;
@@ -272,7 +267,7 @@ class _CartonPackageFormState extends State<CartonPackageForm> {
length: length,
width: width,
height: height,
packages: _packages,
// packages: _packages,
cargoTypes: _cargoTypes,
surchareItems: _surchareItems);
var c = await context.read<CartonModel>().createCarton(carton);

View File

@@ -7,8 +7,6 @@ import '../../../domain/entities/cargo_type.dart';
import '../../../domain/entities/carton_size.dart';
import '../../../domain/entities/fcs_shipment.dart';
import '../../../helpers/theme.dart';
import '../../domain/entities/package.dart';
import '../../domain/entities/user.dart';
import '../main/util.dart';
import '../widgets/local_text.dart';
@@ -24,7 +22,7 @@ class CartonSubmit extends StatelessWidget {
final User consingee;
final String billToValue;
final FcsShipment shipment;
final List<Package> packages;
// final List<Package> packages;
final String cartonSizeType;
final CartonSize? standardSize;
final double length;
@@ -44,7 +42,7 @@ class CartonSubmit extends StatelessWidget {
this.onCreate,
this.onPrevious,
required this.shipment,
this.packages = const [],
// this.packages = const [],
this.standardSize,
required this.cartonSizeType,
required this.lastMile,
@@ -196,42 +194,42 @@ class CartonSubmit extends StatelessWidget {
),
);
final packagesBox = Padding(
padding: const EdgeInsets.only(top: 10),
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Padding(
padding: const EdgeInsets.only(left: 5, bottom: 5),
child: LocalText(context, 'box.package.count',
translationVariables: [packages.length.toString()],
color: primaryColor,
fontSize: 16,
fontWeight: FontWeight.normal),
),
Container(
decoration: BoxDecoration(
border: Border.all(color: primaryColor),
borderRadius: BorderRadius.circular(5),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Wrap(
spacing: 15,
children: packages.map((e) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2.5,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: Text(
e.trackingID ?? "",
style: TextStyle(color: Colors.black, fontSize: 15),
),
),
);
}).toList()),
),
),
]),
);
// final packagesBox = Padding(
// padding: const EdgeInsets.only(top: 10),
// child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
// Padding(
// padding: const EdgeInsets.only(left: 5, bottom: 5),
// child: LocalText(context, 'box.package.count',
// translationVariables: [packages.length.toString()],
// color: primaryColor,
// fontSize: 16,
// fontWeight: FontWeight.normal),
// ),
// Container(
// decoration: BoxDecoration(
// border: Border.all(color: primaryColor),
// borderRadius: BorderRadius.circular(5),
// ),
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: Wrap(
// spacing: 15,
// children: packages.map((e) {
// return SizedBox(
// width: MediaQuery.of(context).size.width / 2.5,
// child: Padding(
// padding: const EdgeInsets.symmetric(vertical: 3),
// child: Text(
// e.trackingID ?? "",
// style: TextStyle(color: Colors.black, fontSize: 15),
// ),
// ),
// );
// }).toList()),
// ),
// ),
// ]),
// );
final cargosBox = Padding(
padding: const EdgeInsets.only(top: 10),
@@ -245,7 +243,8 @@ class CartonSubmit extends StatelessWidget {
color: primaryColor,
fontSize: 16,
fontWeight: FontWeight.normal),
Text("${removeTrailingZeros(totalWeight)} lb",
Text(
"${twoDecimalFormatted(double.tryParse(removeTrailingZeros(totalWeight)) ?? 0)} lb",
style: TextStyle(color: Colors.black, fontSize: 15))
],
),
@@ -275,7 +274,8 @@ class CartonSubmit extends StatelessWidget {
style: TextStyle(
color: Colors.black, fontSize: 15),
),
Text("${removeTrailingZeros(e.weight)} lb",
Text(
"${twoDecimalFormatted(double.tryParse(removeTrailingZeros(e.weight)) ?? 0)} lb",
style: TextStyle(
color: Colors.black, fontSize: 15))
],
@@ -448,11 +448,11 @@ class CartonSubmit extends StatelessWidget {
const SizedBox(height: 10),
shipmentBox,
const SizedBox(height: 10),
packages.isNotEmpty ? packagesBox : const SizedBox(),
// packages.isNotEmpty ? packagesBox : const SizedBox(),
// const SizedBox(height: 10),
cargoTypes.isNotEmpty ? cargosBox : const SizedBox(),
const SizedBox(height: 10),
cargosBox,
const SizedBox(height: 10),
surChargeItemsBox,
surchareItems.isNotEmpty ? surChargeItemsBox : const SizedBox(),
const SizedBox(height: 20),
],
),

View File

@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:intl/intl.dart';
import '../main/util.dart';
import '../widgets/local_text.dart';
import 'carton_info.dart';
@@ -99,7 +100,7 @@ class MixCartonDetailList extends StatelessWidget {
),
),
Text(
"${carton.cartonWeight.toStringAsFixed(2)} lb",
"${twoDecimalFormatted(carton.cartonWeight)} lb",
style: TextStyle(fontSize: 14.0, color: Colors.grey),
)
],

View File

@@ -5,59 +5,68 @@ import 'package:logging/logging.dart';
import '../../../constants.dart';
import '../../../domain/entities/package.dart';
import '../../../pagination/paginator_listener.dart';
import '../../main/model/base_model.dart';
class PackageSelectionModel extends BaseModel {
final log = Logger("PackageSelectionModel");
String query = "";
List<Package> packages = [];
List<Package> selectedPackages = [];
PaginatorListener<Package>? getPackages;
List<Package> packages = [];
bool isLoading = false;
DocumentSnapshot? _lastDocument;
bool ended = false;
Timer? t;
search(String term,
{bool imm = false,
required String shipmentId,
required String senderId,
required String consigneeId}) async {
query = term;
@override
logout() async {
packages.clear();
_lastDocument = null;
ended = false;
t?.cancel();
t = Timer(Duration(milliseconds: imm ? 0 : 800), () async {
await refresh(
term: term,
shipmentId: shipmentId,
consigneeId: consigneeId,
senderId: senderId);
});
getPackages?.close();
}
loadPackages(
{required String senderId,
required String consigneeId,
required String shipmentId}) {
if (user == null) return;
String path = "/$packages_collection";
Query col = FirebaseFirestore.instance
.collection(path)
.where("sender_id", isEqualTo: senderId)
.where("user_id", isEqualTo: consigneeId)
.where("fcs_shipment_id", isEqualTo: shipmentId);
Query pageQuery = FirebaseFirestore.instance
.collection(path)
.where("sender_id", isEqualTo: senderId)
.where("user_id", isEqualTo: consigneeId)
.where("fcs_shipment_id", isEqualTo: shipmentId);
pageQuery = pageQuery.orderBy("created_date", descending: true);
getPackages?.close();
getPackages = PaginatorListener<Package>(
col, pageQuery, (data, id) => Package.fromMap(data, id),
rowPerLoad: 30);
}
Future<void> refresh(
{required String shipmentId,
required String senderId,
required String consigneeId,
String term = ""}) async {
required String consigneeId}) async {
packages.clear();
_lastDocument = null;
ended = false;
await loadMoreData(
shipmentId: shipmentId,
senderId: senderId,
consigneeId: consigneeId,
term: term);
shipmentId: shipmentId,
senderId: senderId,
consigneeId: consigneeId,
);
notifyListeners();
}
Future<void> loadMoreData(
{required String shipmentId,
required String senderId,
required String consigneeId,
String term = ""}) async {
required String consigneeId}) async {
int rowPerPage = 20;
try {
@@ -69,12 +78,9 @@ class PackageSelectionModel extends BaseModel {
whereIn: [package_processed_status, package_packed_status])
.where("sender_id", isEqualTo: senderId)
.where("user_id", isEqualTo: consigneeId)
.where("fcs_shipment_id", isEqualTo: shipmentId)
.where("is_deleted", isEqualTo: false);
if (term != "") {
query = query.where("tracking_id", isEqualTo: term);
}
query = query.orderBy("created_date", descending: true);
if (_lastDocument != null) {
@@ -92,10 +98,6 @@ class PackageSelectionModel extends BaseModel {
return p;
}).toList();
for (var p in list) {
selectedPackages.contains(p) ? p.isChecked = true : p.isChecked = false;
}
packages.addAll(list);
if (list.length < rowPerPage) ended = true;
notifyListeners();
@@ -106,22 +108,8 @@ class PackageSelectionModel extends BaseModel {
}
}
selectPackage(Package a) {
if (a.isChecked) {
selectedPackages.add(a);
} else {
selectedPackages.remove(a);
}
}
addSelectedPackage(List<Package> list) {
selectedPackages = list;
}
clearSelection() {
selectedPackages.clear();
packages.clear();
query = "";
}
Future<List<Package>> getPackagesBySenderAndConsigneeId(
@@ -174,4 +162,21 @@ class PackageSelectionModel extends BaseModel {
}
return list;
}
Future<int?> getPackageCount(
{required String senderId,
required String consigneeId,
required String shipmentId}) async {
String path = "/$packages_collection";
AggregateQuerySnapshot query = await FirebaseFirestore.instance
.collection(path)
.where("sender_id", isEqualTo: senderId)
.where("user_id", isEqualTo: consigneeId)
.where("fcs_shipment_id", isEqualTo: shipmentId)
.count()
.get();
return query.count;
}
}

View File

@@ -1,24 +1,54 @@
import 'package:fcs/domain/entities/package.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/carton/model/package_selection_model.dart';
import 'package:fcs/pages/package/package_info.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import '../../pagination/paginator_listview.dart';
import '../widgets/local_text.dart';
class PackageDetailList extends StatelessWidget {
class PackageDetailList extends StatefulWidget {
final String cartonNumber;
final List<Package> packages;
PackageDetailList(
{super.key, required this.cartonNumber, required this.packages});
final int packageCount;
final String senderID;
final String consingeeID;
final String fcsShipmentID;
const PackageDetailList(
{super.key,
required this.cartonNumber,
required this.packageCount,
required this.senderID,
required this.consingeeID,
required this.fcsShipmentID});
@override
State<PackageDetailList> createState() => _PackageDetailListState();
}
class _PackageDetailListState extends State<PackageDetailList> {
final NumberFormat numberFormatter = NumberFormat("#,###");
@override
void initState() {
_init();
super.initState();
}
_init() {
context.read<PackageSelectionModel>().loadPackages(
senderId: widget.senderID,
consigneeId: widget.consingeeID,
shipmentId: widget.fcsShipmentID);
}
@override
Widget build(BuildContext context) {
var packageModel = context.watch<PackageSelectionModel>();
return Scaffold(
appBar: LocalAppBar(
backgroundColor: Colors.white,
@@ -31,81 +61,81 @@ class PackageDetailList extends StatelessWidget {
"box.package.count",
fontSize: 20,
color: primaryColor,
translationVariables: [numberFormatter.format(packages.length)],
translationVariables: [
numberFormatter.format(widget.packageCount)
],
),
Text(cartonNumber,
Text(widget.cartonNumber,
style: TextStyle(fontSize: 15, color: Colors.black))
],
),
),
body: ListView.separated(
separatorBuilder: (context, index) =>
Divider(height: 1, color: dividerColor),
itemCount: packages.length,
itemBuilder: (BuildContext context, int index) {
var package = packages[index];
return InkWell(
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => PackageInfo(package: package)),
);
},
child: Container(
padding: EdgeInsets.only(left: 15, right: 15),
child: Column(
children: [
Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
children: <Widget>[
Icon(Octicons.package, color: primaryColor),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 15),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
package.id == null
? ''
: package.trackingID!,
style: TextStyle(
fontSize: 15.0,
color: Colors.black),
),
Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(
package.market == null
body: PaginatorListView<Package>(
paginatorListener: packageModel.getPackages!,
rowBuilder: (p) {
Package package = p;
return InkWell(
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => PackageInfo(package: package)),
);
},
child: Container(
padding: EdgeInsets.only(left: 15, right: 15),
child: Column(
children: [
Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
children: <Widget>[
Icon(Octicons.package, color: primaryColor),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 15),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
package.id == null
? ''
: package.market!,
: package.trackingID!,
style: TextStyle(
fontSize: 15.0,
color: Colors.grey),
color: Colors.black),
),
),
],
Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(
package.market == null
? ''
: package.market!,
style: TextStyle(
fontSize: 15.0,
color: Colors.grey),
),
),
],
),
),
),
),
],
],
),
),
),
),
],
),
],
],
),
],
),
),
),
);
},
));
);
},
color: primaryColor));
}
}

View File

@@ -74,32 +74,38 @@ class PackageSelectionResult extends StatelessWidget {
child: Row(
children: <Widget>[
Expanded(
child: new Padding(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0),
child: new Column(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
new Text(package.trackingID ?? "",
style: new TextStyle(
Text(package.trackingID ?? "",
style: TextStyle(
fontSize: 15.0,
color: Colors.black)),
new Text(
package.cartonIds.isEmpty
? "-"
: "${numberFormatter.format(package.cartonIds.length)} Boxes",
style: new TextStyle(
Text(
package.market != null &&
package.market != ""
? "${package.market}"
: "-",
style: TextStyle(
fontSize: 15.0,
color: Colors.grey),
),
// Text(
// package.cartonIds.isEmpty
// ? "-"
// : "${numberFormatter.format(package.cartonIds.length)} Boxes",
// style: TextStyle(
// fontSize: 15.0,
// color: Colors.grey),
// ),
],
),
),
),
package.isChecked
? Icon(Icons.check, color: primaryColor)
: const SizedBox()
],
),
),

View File

@@ -1,5 +1,5 @@
import 'package:fcs/domain/entities/fcs_shipment.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart';
@@ -8,27 +8,25 @@ import 'package:provider/provider.dart';
import '../../domain/entities/package.dart';
import '../../domain/entities/user.dart';
import '../main/util.dart';
import '../widgets/barcode_scanner.dart';
import '../package/package_info.dart';
import '../widgets/continue_button.dart';
import '../widgets/local_title.dart';
import '../widgets/previous_button.dart';
import 'model/package_selection_model.dart';
import 'package_selection_result.dart';
typedef OnPrevious = Function(List<Package> packages);
typedef OnContinue = Function(List<Package> packages);
typedef OnPrevious = Function();
typedef OnContinue = Function();
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({
super.key,
required this.packages,
this.onPrevious,
this.onContinue,
required this.shipment,
@@ -41,8 +39,6 @@ class PackageSelectionWidget extends StatefulWidget {
}
class _PackageSelectionWidgetState extends State<PackageSelectionWidget> {
final TextEditingController _controller = TextEditingController();
String _query = "";
bool _isLoadMore = false;
final _scrollController = ScrollController();
bool _down = true;
@@ -60,29 +56,18 @@ class _PackageSelectionWidgetState extends State<PackageSelectionWidget> {
}
_init() {
var searchModel = context.read<PackageSelectionModel>();
_controller.text = searchModel.query;
_query = searchModel.query;
searchModel.refresh(
shipmentId: widget.shipment.id!,
consigneeId: widget.consignee.id!,
senderId: widget.sender.id!,
term: _query);
searchModel.addSelectedPackage(widget.packages);
var packageModel = context.read<PackageSelectionModel>();
packageModel.refresh(
shipmentId: widget.shipment.id!,
consigneeId: widget.consignee.id!,
senderId: widget.sender.id!,
);
if (mounted) {
setState(() {});
}
}
// @override
// void didUpdateWidget(covariant PackageSelectionWidget oldWidget) {
// _init();
// super.didUpdateWidget(oldWidget);
// }
Future<void> _loadMoreData() async {
if (_isLoadMore) return;
var model = context.read<PackageSelectionModel>();
@@ -92,10 +77,10 @@ class _PackageSelectionWidgetState extends State<PackageSelectionWidget> {
});
await model.loadMoreData(
shipmentId: widget.shipment.id!,
consigneeId: widget.consignee.id!,
senderId: widget.sender.id!,
term: _query);
shipmentId: widget.shipment.id!,
consigneeId: widget.consignee.id!,
senderId: widget.sender.id!,
);
setState(() {
_isLoadMore = false;
@@ -105,8 +90,7 @@ class _PackageSelectionWidgetState extends State<PackageSelectionWidget> {
@override
Widget build(BuildContext context) {
var model = context.watch<PackageSelectionModel>();
List<Package> searchResults = model.packages;
List<Package> selectedPackageList = model.selectedPackages;
List<Package> packages = model.packages;
final senderBox = userDisplayBox(context,
lableKey: "box.sender.title",
@@ -132,103 +116,23 @@ class _PackageSelectionWidgetState extends State<PackageSelectionWidget> {
final continueBtn = ContinueButton(
onTap: () {
if (selectedPackageList.isEmpty || searchResults.isEmpty) {
showMsgDialog(context, 'Error', "Please select the packages");
if (packages.isEmpty) {
showMsgDialog(context, 'Error', "Please add the packages");
return false;
}
if (widget.onContinue != null) {
widget.onContinue!(selectedPackageList);
widget.onContinue!();
}
},
);
final previousBtn = PreviousButton(onTap: () {
if (widget.onPrevious != null) {
widget.onPrevious!(selectedPackageList);
widget.onPrevious!();
}
});
final searchBox = SizedBox(
height: 40,
child: TextField(
controller: _controller,
cursorColor: primaryColor,
onSubmitted: (value) {
setState(() {
_query = value;
});
_search(imm: true);
},
onChanged: (v) {
setState(() {
_query = v;
});
_search();
},
decoration: InputDecoration(
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: labelColor),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
borderSide:
BorderSide(color: labelColor.withOpacity(0.3), width: 0)),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
borderSide: BorderSide(color: labelColor),
),
hintText: "Search by tracking number",
hintStyle: const TextStyle(
color: Colors.grey,
fontSize: 16,
fontWeight: FontWeight.normal),
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
children: [
InkResponse(
radius: 20,
onTap: () {
_scan(context);
},
child: const Icon(Icons.qr_code_scanner,
color: Colors.black87)),
IconButton(
splashRadius: 20,
onPressed: () {
setState(() {
_controller.clear();
_query = "";
});
_search();
},
icon: const Icon(Icons.close, color: Colors.black87)),
],
),
contentPadding: const EdgeInsets.all(10),
filled: true),
),
);
final selectedPackageBox = Align(
alignment: Alignment.topLeft,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
padding: EdgeInsets.only(top: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: selectedPackageList
.map((e) => Padding(
padding: const EdgeInsets.only(right: 20),
child: Text(e.trackingID ?? "",
style:
TextStyle(color: Colors.black, fontSize: 15)),
))
.toList())),
);
return Column(
children: [
Expanded(
@@ -255,8 +159,6 @@ class _PackageSelectionWidgetState extends State<PackageSelectionWidget> {
LocalTitle(
textKey: "box.select.package", topPadding: 5),
const SizedBox(height: 10),
searchBox,
selectedPackageBox,
])
: const SizedBox(),
),
@@ -273,11 +175,12 @@ class _PackageSelectionWidgetState extends State<PackageSelectionWidget> {
_down = true;
});
},
onTap: (a) async {
setState(() {
a.isChecked = !a.isChecked;
});
context.read<PackageSelectionModel>().selectPackage(a);
onTap: (a) {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => PackageInfo(package: a)),
);
},
),
),
@@ -302,31 +205,4 @@ class _PackageSelectionWidgetState extends State<PackageSelectionWidget> {
],
);
}
_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<PackageSelectionModel>().search(_query,
imm: imm,
shipmentId: widget.shipment.id!,
senderId: widget.sender.id!,
consigneeId: widget.consignee.id!);
} catch (e) {
showMsgDialog(context, 'Error', e.toString());
}
}
}

View File

@@ -1,223 +0,0 @@
import 'package:fcs/domain/entities/fcs_shipment.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart';
import 'package:provider/provider.dart';
import '../../domain/entities/package.dart';
import '../../domain/entities/user.dart';
import '../main/util.dart';
import '../widgets/continue_button.dart';
import '../widgets/local_text.dart';
import '../widgets/local_title.dart';
import '../widgets/previous_button.dart';
import 'model/package_selection_model.dart';
import 'package_selection_result.dart';
typedef OnPrevious = Function(List<Package> packages);
typedef OnContinue = Function(List<Package> packages);
class PackagesWidget extends StatefulWidget {
final User sender;
final User consignee;
final FcsShipment shipment;
final OnPrevious? onPrevious;
final OnContinue? onContinue;
const PackagesWidget({
super.key,
this.onPrevious,
this.onContinue,
required this.shipment,
required this.sender,
required this.consignee,
});
@override
State<PackagesWidget> createState() => _PackagesWidgetState();
}
class _PackagesWidgetState extends State<PackagesWidget> {
final _scrollController = ScrollController();
bool _down = true;
List<Package> _packages = [];
bool _isLoading = false;
@override
void initState() {
_init();
super.initState();
_scrollController.addListener(() {
setState(() {
_down = _scrollController.position.userScrollDirection ==
ScrollDirection.forward;
});
});
}
_init() async {
_packages.clear();
_isLoading = true;
var packageModel = context.read<PackageSelectionModel>();
var list = await packageModel.getActivePackages(
shipmentId: widget.shipment.id!,
senderId: widget.sender.id!,
consigneeId: widget.consignee.id!);
_packages = List.from(list);
_isLoading = false;
if (mounted) {
setState(() {});
}
}
@override
Widget build(BuildContext context) {
final senderBox = userDisplayBox(context,
lableKey: "box.sender.title",
icon: MaterialCommunityIcons.account_arrow_right,
showLink: false,
name: widget.sender.name ?? "",
fcsID: widget.sender.fcsID ?? "");
final consigneeBox = userDisplayBox(context,
showLink: false,
lableKey: "box.consignee.title",
icon: MaterialCommunityIcons.account_arrow_left,
name: widget.consignee.name,
fcsID: widget.consignee.fcsID);
final userRow = Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(flex: 2, child: consigneeBox),
Flexible(child: senderBox)
],
);
final continueBtn = ContinueButton(
onTap: () {
if (_packages.isEmpty) {
showMsgDialog(context, 'Error', "Please add the packages");
return false;
}
if (widget.onContinue != null) {
widget.onContinue!(_packages);
}
},
);
final previousBtn = PreviousButton(onTap: () {
if (widget.onPrevious != null) {
widget.onPrevious!(_packages);
}
});
return Column(
children: [
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 10, right: 10),
child: Column(
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder:
(Widget child, Animation<double> animation) =>
FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
axis: Axis.vertical,
child: child,
),
),
child: _down
? Column(children: [
const SizedBox(height: 8),
userRow,
LocalTitle(
textKey: "box.select.package", topPadding: 5),
const SizedBox(height: 10),
])
: const SizedBox(),
),
Expanded(
child: _packages.isEmpty && !_isLoading
? Center(
child: LocalText(context, 'box.no_package',
color: Colors.black, fontSize: 15))
: RefreshIndicator(
color: primaryColor,
onRefresh: () async {
_init();
},
child: ListView.builder(
padding: const EdgeInsets.only(top: 10),
controller: _scrollController,
shrinkWrap: true,
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (context, index) {
Package package = _packages[index];
return packageRow(context, package);
},
itemCount: _packages.length)),
),
],
),
),
),
widget.onContinue != null
? Padding(
padding: const EdgeInsets.only(left: 15, right: 15, top: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
previousBtn,
continueBtn,
],
),
)
: const SizedBox(),
const SizedBox(height: 20)
],
);
}
Widget packageRow(BuildContext context, Package package) {
return Padding(
padding: const EdgeInsets.only(top: 5, bottom: 5),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5)),
border: Border.all(color: Colors.grey.shade300)),
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(package.trackingID ?? "",
style: TextStyle(fontSize: 15.0, color: Colors.black)),
Text(
package.cartonIds.isEmpty
? "-"
: "${numberFormatter.format(package.cartonIds.length)} Boxes",
style: TextStyle(fontSize: 15.0, color: Colors.grey),
),
],
),
),
),
package.isChecked
? Icon(Icons.check, color: primaryColor)
: const SizedBox()
],
),
),
);
}
}

View File

@@ -4,15 +4,16 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:intl/intl.dart';
import '../../main/util.dart';
import '../carton_info.dart';
import '../print_qr_code_page.dart';
class CartonListRow extends StatelessWidget {
final Carton box;
CartonListRow({Key? key, required this.box}) : super(key: key);
CartonListRow({super.key, required this.box});
final double dotSize = 15.0;
final DateFormat dateFormat = new DateFormat("dd MMM yyyy");
final DateFormat dateFormat = DateFormat("dd MMM yyyy");
@override
Widget build(BuildContext context) {
@@ -28,9 +29,9 @@ class CartonListRow extends StatelessWidget {
child: Row(
children: <Widget>[
Expanded(
child: new Padding(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
child: Row(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 0),
@@ -40,24 +41,24 @@ class CartonListRow extends StatelessWidget {
size: 30,
),
),
new Expanded(
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 15.0),
child: Row(
children: [
new Column(
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
Text(
box.cartonNumber ?? "",
style: new TextStyle(
style: TextStyle(
fontSize: 15.0, color: Colors.black),
),
Padding(
padding: const EdgeInsets.only(top: 5),
child: new Text(
child: Text(
box.consigneeName ?? "",
style: new TextStyle(
style: TextStyle(
fontSize: 15.0, color: Colors.grey),
),
),
@@ -95,10 +96,9 @@ class CartonListRow extends StatelessWidget {
padding: const EdgeInsets.only(top: 5),
child: Row(
children: <Widget>[
new Text(
"${box.cartonWeight.toStringAsFixed(2)} lb",
style:
new TextStyle(fontSize: 14.0, color: Colors.grey),
Text(
"${twoDecimalFormatted(box.cartonWeight)} lb",
style: TextStyle(fontSize: 14.0, color: Colors.grey),
),
],
),

View File

@@ -410,6 +410,11 @@ String removeTrailingZeros(double number) {
return result;
}
String twoDecimalFormatted(double number) {
String formattedString = number.toStringAsFixed(2);
return formattedString;
}
bool isValidEmail(String email) {
// Define a regular expression for validating an email
final emailRegex = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$');
@@ -561,4 +566,3 @@ Widget settingRow(BuildContext context,
),
);
}