Merge branch 'master' of tzw/fcs into master

This commit is contained in:
2024-02-19 19:24:39 +06:30
committed by Gogs
6 changed files with 316 additions and 189 deletions

View File

@@ -8,7 +8,7 @@ const buttonColor = const Color(0xFFFFFFFF);
const buttonBkColor = const Color(0xFF268944); const buttonBkColor = const Color(0xFF268944);
const labelColor = const Color(0xFF757575); const labelColor = const Color(0xFF757575);
var dividerColor = Colors.grey.shade400; var dividerColor = Colors.grey.shade400;
const dangerColor= const Color(0xffff0606); const dangerColor = const Color(0xffff0606);
const TextStyle labelStyle = const TextStyle labelStyle =
TextStyle(fontSize: 20, color: labelColor, fontWeight: FontWeight.w500); TextStyle(fontSize: 20, color: labelColor, fontWeight: FontWeight.w500);

View File

@@ -26,6 +26,7 @@ class FcsShipmentEditor extends StatefulWidget {
} }
class _FcsShipmentEditorState extends State<FcsShipmentEditor> { class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
final _formKey = GlobalKey<FormState>();
var dateFormatter = new DateFormat('dd MMM yyyy'); var dateFormatter = new DateFormat('dd MMM yyyy');
TextEditingController _shipmentNumberController = new TextEditingController(); TextEditingController _shipmentNumberController = new TextEditingController();
TextEditingController _cutoffDateController = new TextEditingController(); TextEditingController _cutoffDateController = new TextEditingController();
@@ -111,63 +112,86 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
}), }),
body: ListView( body: Form(
padding: const EdgeInsets.only(left: 10, right: 10), key: _formKey,
children: <Widget>[ child: ListView(
InputText( padding: const EdgeInsets.only(left: 10, right: 10),
labelTextKey: "FCSshipment.number", children: <Widget>[
iconData: Ionicons.ios_airplane, InputText(
controller: _shipmentNumberController, labelTextKey: "FCSshipment.number",
), iconData: Ionicons.ios_airplane,
InputDate( controller: _shipmentNumberController,
labelTextKey: "FCSshipment.cutoff_date", validator: (value) {
iconData: Icons.date_range, if (value!.isEmpty) {
controller: _cutoffDateController, return "Enter shipment number";
), }
InputDate( return null;
labelTextKey: "FCSshipment.ETA", },
iconData: Icons.date_range, ),
controller: _arrivalDateController, InputDate(
), labelTextKey: "FCSshipment.cutoff_date",
DropdownButtonFormField( iconData: Icons.date_range,
value: _currentShipmentType == "" ? null : _currentShipmentType, controller: _cutoffDateController,
decoration: InputDecoration( validator: (value) {
enabledBorder: UnderlineInputBorder( if (value!.isEmpty) {
borderSide: BorderSide(color: primaryColor)), return "Select cutoff date";
focusedBorder: UnderlineInputBorder( }
borderSide: BorderSide(color: primaryColor)), return null;
fillColor: Colors.white, },
labelStyle: languageModel.isEng ),
? newLabelStyle(color: Colors.black54, fontSize: 20) InputDate(
: newLabelStyleMM(color: Colors.black54, fontSize: 20), labelTextKey: "FCSshipment.ETA",
labelText: AppTranslations.of(context)! iconData: Icons.date_range,
.text('FCSshipment.shipment_type'), controller: _arrivalDateController,
icon: Icon(Ionicons.ios_airplane, color: primaryColor)), validator: (value) {
items: mainModel.setting!.shipmentTypes if (value!.isEmpty) {
.map((e) => DropdownMenuItem(child: Text(e), value: e)) return "Select ETA date";
.toList(), }
onChanged: (String? selected) => { return null;
setState(() { },
_currentShipmentType = selected; ),
}) DropdownButtonFormField(
}, value:
), _currentShipmentType == "" ? null : _currentShipmentType,
InputText( decoration: InputDecoration(
labelTextKey: 'FCSshipment.consignee', enabledBorder: UnderlineInputBorder(
iconData: Icons.work, borderSide: BorderSide(color: primaryColor)),
controller: _consigneeController), focusedBorder: UnderlineInputBorder(
InputText( borderSide: BorderSide(color: primaryColor)),
labelTextKey: 'FCSshipment.port_of_loading', fillColor: Colors.white,
iconData: FontAwesomeIcons.ship, labelStyle: languageModel.isEng
controller: _portController), ? newLabelStyle(color: Colors.black54, fontSize: 20)
InputText( : newLabelStyleMM(
labelTextKey: 'FCSshipment.final_destination', color: Colors.black54, fontSize: 20),
iconData: MaterialCommunityIcons.location_enter, labelText: AppTranslations.of(context)!
controller: _destinationController), .text('FCSshipment.shipment_type'),
SizedBox(height: 20), icon: Icon(Ionicons.ios_airplane, color: primaryColor)),
_isNew ? createBtn : updateBtn, items: mainModel.setting!.shipmentTypes
SizedBox(height: 15) .map((e) => DropdownMenuItem(child: Text(e), value: e))
]), .toList(),
onChanged: (String? selected) => {
setState(() {
_currentShipmentType = selected;
})
},
),
InputText(
labelTextKey: 'FCSshipment.consignee',
iconData: Icons.work,
controller: _consigneeController),
InputText(
labelTextKey: 'FCSshipment.port_of_loading',
iconData: FontAwesomeIcons.ship,
controller: _portController),
InputText(
labelTextKey: 'FCSshipment.final_destination',
iconData: MaterialCommunityIcons.location_enter,
controller: _destinationController),
SizedBox(height: 20),
_isNew ? createBtn : updateBtn,
SizedBox(height: 15)
]),
),
), ),
); );
} }
@@ -180,45 +204,20 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
fcsShipment.consignee = _consigneeController.text; fcsShipment.consignee = _consigneeController.text;
fcsShipment.port = _portController.text; fcsShipment.port = _portController.text;
fcsShipment.destination = _destinationController.text; fcsShipment.destination = _destinationController.text;
try {
var cutoffDate = _cutoffDateController.text; var cutoffDate = _cutoffDateController.text;
var arrivalDate = _arrivalDateController.text; var arrivalDate = _arrivalDateController.text;
fcsShipment.cutoffDate = fcsShipment.cutoffDate =
(cutoffDate == "" ? null : dateFormatter.parse(cutoffDate))!; (cutoffDate == "" ? null : dateFormatter.parse(cutoffDate))!;
fcsShipment.arrivalDate = fcsShipment.arrivalDate =
(arrivalDate == "" ? null : dateFormatter.parse(arrivalDate))!; (arrivalDate == "" ? null : dateFormatter.parse(arrivalDate))!;
} catch (e) {
// showMsgDialog(context, "Error", e.toString()); // shold never happen
}
return fcsShipment; return fcsShipment;
} }
Future<bool> _validate(FcsShipment fcsShipment) async {
if (fcsShipment.shipmentNumber == "") {
await showMsgDialog(context, "Error", "Invalid shipment number!");
return false;
}
if (fcsShipment.shipType == null) {
await showMsgDialog(context, "Error", "Invalid shipment type!");
return false;
}
if (fcsShipment.cutoffDate == null) {
await showMsgDialog(context, "Error", "Invalid cutoff date!");
return false;
}
if (fcsShipment.arrivalDate == null) {
await showMsgDialog(context, "Error", "Invalid ETA date!");
return false;
}
return true;
}
Future<void> _create() async { Future<void> _create() async {
if (!_formKey.currentState!.validate()) return;
FcsShipment fcsShipment = _getPayload(); FcsShipment fcsShipment = _getPayload();
bool valid = await _validate(fcsShipment);
if (!valid) {
return;
}
setState(() { setState(() {
_isLoading = true; _isLoading = true;
}); });
@@ -236,11 +235,9 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
} }
Future<void> _update() async { Future<void> _update() async {
if (!_formKey.currentState!.validate()) return;
FcsShipment fcsShipment = _getPayload(); FcsShipment fcsShipment = _getPayload();
bool valid = await _validate(fcsShipment);
if (!valid) {
return;
}
setState(() { setState(() {
_isLoading = true; _isLoading = true;
}); });

View File

@@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
import 'package:pin_input_text_field/pin_input_text_field.dart'; import 'package:pin_input_text_field/pin_input_text_field.dart';
class PinLoginPage extends StatefulWidget { class PinLoginPage extends StatefulWidget {
//final User user; //final User user;
const PinLoginPage({super.key}); const PinLoginPage({super.key});
@override @override
@@ -16,8 +16,9 @@ class PinLoginPage extends StatefulWidget {
class _PinLoginPageState extends State<PinLoginPage> { class _PinLoginPageState extends State<PinLoginPage> {
bool _isLoading = false; bool _isLoading = false;
late String pin; String pin = "";
//late User _user; //late User _user;
final _formKey = GlobalKey<FormState>();
TextEditingController _fcsIdCtl = new TextEditingController(); TextEditingController _fcsIdCtl = new TextEditingController();
@override @override
@@ -34,6 +35,13 @@ class _PinLoginPageState extends State<PinLoginPage> {
final fcsIdBox = TextFormField( final fcsIdBox = TextFormField(
controller: _fcsIdCtl, controller: _fcsIdCtl,
autofocus: true, autofocus: true,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter FCS ID';
}
return null;
},
style: TextStyle( style: TextStyle(
fontSize: 15, color: Colors.black87, fontWeight: FontWeight.w500), fontSize: 15, color: Colors.black87, fontWeight: FontWeight.w500),
cursorColor: primaryColor, cursorColor: primaryColor,
@@ -47,6 +55,7 @@ class _PinLoginPageState extends State<PinLoginPage> {
borderSide: BorderSide(color: primaryColor, width: 1.0)), borderSide: BorderSide(color: primaryColor, width: 1.0)),
disabledBorder: UnderlineInputBorder( disabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)), borderSide: BorderSide(color: primaryColor, width: 1.0)),
errorStyle: TextStyle(color: dangerColor),
), ),
); );
@@ -60,6 +69,46 @@ class _PinLoginPageState extends State<PinLoginPage> {
fit: BoxFit.fitHeight, fit: BoxFit.fitHeight,
), ),
); );
final pinInputBox = FormField(
validator: (value) {
if (pin == "") {
return "Please enter PIN";
}
if (pin.length < 6) {
return "PIN must be 6 digit";
}
return null;
},
autovalidateMode: AutovalidateMode.onUserInteraction,
builder: (state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PinInputTextField(
cursor: Cursor(
color: primaryColor, enabled: true, width: 2, height: 23),
pinLength: 6,
decoration: BoxLooseDecoration(
strokeColorBuilder: PinListenColorBuilder(
primaryColor, Colors.grey.shade400),
errorTextStyle: TextStyle(color: dangerColor)),
textInputAction: TextInputAction.done,
autoFocus: false,
onChanged: _pinChange,
),
Padding(
padding: const EdgeInsets.only(top: 8),
child: pin == "" || pin.length < 6
? SizedBox(
height: 20,
child: Text(state.errorText ?? "",
style: TextStyle(color: dangerColor, fontSize: 12)),
)
: const SizedBox(height: 20),
)
],
);
});
final loginBtn = Padding( final loginBtn = Padding(
padding: EdgeInsets.only(top: 30), padding: EdgeInsets.only(top: 30),
@@ -74,48 +123,40 @@ class _PinLoginPageState extends State<PinLoginPage> {
// appBar: LocalAppBar( // appBar: LocalAppBar(
// backgroundColor: primaryColor, // backgroundColor: primaryColor,
// ), // ),
body: ListView( body: Form(
padding: EdgeInsets.only(top: 80, left: 15, right: 15, bottom: 20), key: _formKey,
children: [ child: ListView(
pinLoginLogo, padding: EdgeInsets.only(top: 80, left: 15, right: 15, bottom: 20),
Padding( children: [
padding: EdgeInsets.only(top: 20, bottom: 20), pinLoginLogo,
child: Center( Padding(
child: LocalText(context, "welcome.pinlogin", padding: EdgeInsets.only(top: 20, bottom: 20),
color: Colors.black, fontSize: 18), child: Center(
), child: LocalText(context, "welcome.pinlogin",
color: Colors.black, fontSize: 18),
), ),
LocalText( ),
context, LocalText(
"welcome.pinlogin.fcsid", context,
color: Colors.black54, "welcome.pinlogin.fcsid",
fontSize: 15, color: Colors.black54,
), fontSize: 15,
fcsIdBox, ),
Padding( fcsIdBox,
padding: EdgeInsets.only(top: 25, bottom: 20), Padding(
child: LocalText( padding: EdgeInsets.only(top: 25, bottom: 20),
context, child: LocalText(
"welcome.pinlogin.pin", context,
color: Colors.black54, "welcome.pinlogin.pin",
fontSize: 15, color: Colors.black54,
)), fontSize: 15,
PinInputTextField( )),
cursor: Cursor( pinInputBox,
color: primaryColor, enabled: true, width: 2, height: 23), loginBtn,
pinLength: 6, ],
decoration: BoxLooseDecoration( ),
strokeColorBuilder: PinListenColorBuilder( )),
primaryColor, Colors.grey.shade400)),
textInputAction: TextInputAction.done,
autoFocus: false,
onChanged: _pinChange,
),
loginBtn,
],
)),
); );
} }
@@ -124,16 +165,20 @@ class _PinLoginPageState extends State<PinLoginPage> {
this.pin = pin; this.pin = pin;
}); });
} }
_login() async { _login() async {
if (pin == "") { if (pin == "" && _formKey.currentState!.validate()) {
showMsgDialog(context, "Error", "Invalid PIN"); showMsgDialog(context, "Error", "Invalid PIN");
return; return;
} }
if (pin.length < 6) { if (pin.length < 6 && _formKey.currentState!.validate()) {
showMsgDialog(context, "Error", "PIN must be 6 digits"); showMsgDialog(context, "Error", "PIN must be 6 digits");
return; return;
} }
if (!_formKey.currentState!.validate()) {
return;
}
setState(() { setState(() {
_isLoading = true; _isLoading = true;

View File

@@ -16,6 +16,7 @@ class StaffPinEditor extends StatefulWidget {
} }
class _StaffPinEditorState extends State<StaffPinEditor> { class _StaffPinEditorState extends State<StaffPinEditor> {
final _formKey = GlobalKey<FormState>();
bool _isLoading = false; bool _isLoading = false;
late User _staff; late User _staff;
bool _enablePinLogin = false; bool _enablePinLogin = false;
@@ -110,28 +111,101 @@ class _StaffPinEditorState extends State<StaffPinEditor> {
backgroundColor: Colors.white, backgroundColor: Colors.white,
labelColor: primaryColor, labelColor: primaryColor,
arrowColor: primaryColor), arrowColor: primaryColor),
body: Padding( body: Form(
padding: const EdgeInsets.only(left: 15.0, right: 15), key: _formKey,
child: ListView( child: Padding(
children: <Widget>[ padding: const EdgeInsets.only(left: 15.0, right: 15),
Column( child: ListView(
children: [ children: <Widget>[
Text(_staff.name ?? "", Column(
style: TextStyle(color: Colors.black, fontSize: 18)), children: [
Text(_staff.fcsID ?? "", Text(_staff.name ?? "",
style: TextStyle(color: Colors.black, fontSize: 15)), style: TextStyle(color: Colors.black, fontSize: 18)),
], Text(_staff.fcsID ?? "",
), style: TextStyle(color: Colors.black, fontSize: 15)),
const SizedBox(height: 20), ],
enablePinBox, ),
const SizedBox(height: 30), const SizedBox(height: 20),
newPinBox, enablePinBox,
SizedBox(height: 30), const SizedBox(height: 30),
confirmPinBox, FormField(
SizedBox(height: 30), builder: (FormFieldState<bool> state) {
saveButton, return Column(
SizedBox(height: 30) crossAxisAlignment: CrossAxisAlignment.start,
], children: [
newPinBox,
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: _newPin == '' || _newPin.length < 6
? SizedBox(
height: 20,
child: Text(
state.errorText ?? '',
style: const TextStyle(
color: dangerColor, fontSize: 12),
),
)
: const SizedBox(height: 20),
),
],
);
},
validator: (value) {
if (_newPin == "") {
return 'Enter new PIN';
}
if (_newPin.length < 6) {
return 'New PIN must be 6 digits';
}
return null;
},
),
SizedBox(height: 10),
FormField(
builder: (FormFieldState<bool> state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
confirmPinBox,
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: _confirmPin == '' ||
_confirmPin.length < 6 ||
_confirmPin != _newPin
? SizedBox(
height: 20,
child: Text(
state.errorText ?? '',
style: const TextStyle(
color: dangerColor, fontSize: 12),
),
)
: const SizedBox(height: 20)),
],
);
},
validator: (value) {
if (_confirmPin == "") {
return 'Enter confirm PIN';
}
if (_confirmPin.length < 6) {
return 'Confirm PIN must be 6 digits';
}
if (_confirmPin != _newPin) {
return "Those pins didnt match. Try again.";
}
return null;
},
),
SizedBox(height: 20),
saveButton,
SizedBox(height: 30)
],
),
), ),
), ),
)); ));
@@ -150,30 +224,7 @@ class _StaffPinEditorState extends State<StaffPinEditor> {
} }
_save() async { _save() async {
if (_newPin == "") { if (!_formKey.currentState!.validate()) return;
showMsgDialog(context, "Error", "Invalid new PIN");
return;
}
if (_newPin.length < 6) {
showMsgDialog(context, "Error", "New PIN must be 6 digits");
return;
}
if (_confirmPin == "") {
showMsgDialog(context, "Error", "Invalid confirm PIN");
return;
}
if (_confirmPin.length < 6) {
showMsgDialog(context, "Error", "Confirm PIN must be 6 digits");
return;
}
if (_confirmPin != _newPin) {
showMsgDialog(context, "Error", "Those pins didnt match. Try again.");
return;
}
setState(() { setState(() {
_isLoading = true; _isLoading = true;

View File

@@ -84,6 +84,7 @@ class InputDate extends StatelessWidget {
hintStyle: TextStyle( hintStyle: TextStyle(
height: 1.5, height: 1.5,
), ),
errorStyle: const TextStyle(color: dangerColor, fontSize: 12),
labelText: labelTextKey == null labelText: labelTextKey == null
? null ? null
: AppTranslations.of(context)!.text(labelTextKey!), : AppTranslations.of(context)!.text(labelTextKey!),
@@ -108,6 +109,22 @@ class InputDate extends StatelessWidget {
) )
: UnderlineInputBorder( : UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)), borderSide: BorderSide(color: primaryColor, width: 1.0)),
errorBorder: withBorder
? OutlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? dangerColor, width: 1.0),
)
: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? dangerColor, width: 1.0)),
focusedErrorBorder: withBorder
? OutlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? dangerColor, width: 1.0),
)
: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? dangerColor, width: 1.0)),
), ),
validator: validator), validator: validator),
); );

View File

@@ -57,6 +57,7 @@ class InputText extends StatelessWidget {
constraints: constraints, constraints: constraints,
contentPadding: contentPadding, contentPadding: contentPadding,
// hintText: '', // hintText: '',
errorStyle: const TextStyle(color: dangerColor, fontSize: 12),
hintStyle: TextStyle( hintStyle: TextStyle(
height: 1.5, height: 1.5,
), ),
@@ -96,6 +97,22 @@ class InputText extends StatelessWidget {
: UnderlineInputBorder( : UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: borderColor ?? primaryColor, width: 1.0)), color: borderColor ?? primaryColor, width: 1.0)),
errorBorder: withBorder
? OutlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? dangerColor, width: 1.0),
)
: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? dangerColor, width: 1.0)),
focusedErrorBorder: withBorder
? OutlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? dangerColor, width: 1.0),
)
: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? dangerColor, width: 1.0)),
), ),
validator: validator), validator: validator),
); );