diff --git a/lib/helpers/theme.dart b/lib/helpers/theme.dart index a3104b2..dff3cea 100644 --- a/lib/helpers/theme.dart +++ b/lib/helpers/theme.dart @@ -8,7 +8,7 @@ const buttonColor = const Color(0xFFFFFFFF); const buttonBkColor = const Color(0xFF268944); const labelColor = const Color(0xFF757575); var dividerColor = Colors.grey.shade400; -const dangerColor= const Color(0xffff0606); +const dangerColor = const Color(0xffff0606); const TextStyle labelStyle = TextStyle(fontSize: 20, color: labelColor, fontWeight: FontWeight.w500); diff --git a/lib/pages/fcs_shipment/fcs_shipment_editor.dart b/lib/pages/fcs_shipment/fcs_shipment_editor.dart index 5af5576..70eb196 100644 --- a/lib/pages/fcs_shipment/fcs_shipment_editor.dart +++ b/lib/pages/fcs_shipment/fcs_shipment_editor.dart @@ -26,6 +26,7 @@ class FcsShipmentEditor extends StatefulWidget { } class _FcsShipmentEditorState extends State { + final _formKey = GlobalKey(); var dateFormatter = new DateFormat('dd MMM yyyy'); TextEditingController _shipmentNumberController = new TextEditingController(); TextEditingController _cutoffDateController = new TextEditingController(); @@ -111,63 +112,86 @@ class _FcsShipmentEditorState extends State { Navigator.of(context).pop(); } }), - body: ListView( - padding: const EdgeInsets.only(left: 10, right: 10), - children: [ - InputText( - labelTextKey: "FCSshipment.number", - iconData: Ionicons.ios_airplane, - controller: _shipmentNumberController, - ), - InputDate( - labelTextKey: "FCSshipment.cutoff_date", - iconData: Icons.date_range, - controller: _cutoffDateController, - ), - InputDate( - labelTextKey: "FCSshipment.ETA", - iconData: Icons.date_range, - controller: _arrivalDateController, - ), - DropdownButtonFormField( - value: _currentShipmentType == "" ? null : _currentShipmentType, - decoration: InputDecoration( - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: primaryColor)), - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: primaryColor)), - fillColor: Colors.white, - labelStyle: languageModel.isEng - ? newLabelStyle(color: Colors.black54, fontSize: 20) - : newLabelStyleMM(color: Colors.black54, fontSize: 20), - labelText: AppTranslations.of(context)! - .text('FCSshipment.shipment_type'), - icon: Icon(Ionicons.ios_airplane, color: primaryColor)), - items: mainModel.setting!.shipmentTypes - .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) - ]), + body: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.only(left: 10, right: 10), + children: [ + InputText( + labelTextKey: "FCSshipment.number", + iconData: Ionicons.ios_airplane, + controller: _shipmentNumberController, + validator: (value) { + if (value!.isEmpty) { + return "Enter shipment number"; + } + return null; + }, + ), + InputDate( + labelTextKey: "FCSshipment.cutoff_date", + iconData: Icons.date_range, + controller: _cutoffDateController, + validator: (value) { + if (value!.isEmpty) { + return "Select cutoff date"; + } + return null; + }, + ), + InputDate( + labelTextKey: "FCSshipment.ETA", + iconData: Icons.date_range, + controller: _arrivalDateController, + validator: (value) { + if (value!.isEmpty) { + return "Select ETA date"; + } + return null; + }, + ), + DropdownButtonFormField( + value: + _currentShipmentType == "" ? null : _currentShipmentType, + decoration: InputDecoration( + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: primaryColor)), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: primaryColor)), + fillColor: Colors.white, + labelStyle: languageModel.isEng + ? newLabelStyle(color: Colors.black54, fontSize: 20) + : newLabelStyleMM( + color: Colors.black54, fontSize: 20), + labelText: AppTranslations.of(context)! + .text('FCSshipment.shipment_type'), + icon: Icon(Ionicons.ios_airplane, color: primaryColor)), + items: mainModel.setting!.shipmentTypes + .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 { fcsShipment.consignee = _consigneeController.text; fcsShipment.port = _portController.text; fcsShipment.destination = _destinationController.text; - try { - var cutoffDate = _cutoffDateController.text; - var arrivalDate = _arrivalDateController.text; - fcsShipment.cutoffDate = - (cutoffDate == "" ? null : dateFormatter.parse(cutoffDate))!; - fcsShipment.arrivalDate = - (arrivalDate == "" ? null : dateFormatter.parse(arrivalDate))!; - } catch (e) { - // showMsgDialog(context, "Error", e.toString()); // shold never happen - } + + var cutoffDate = _cutoffDateController.text; + var arrivalDate = _arrivalDateController.text; + fcsShipment.cutoffDate = + (cutoffDate == "" ? null : dateFormatter.parse(cutoffDate))!; + fcsShipment.arrivalDate = + (arrivalDate == "" ? null : dateFormatter.parse(arrivalDate))!; return fcsShipment; } - Future _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 _create() async { + if (!_formKey.currentState!.validate()) return; FcsShipment fcsShipment = _getPayload(); - bool valid = await _validate(fcsShipment); - if (!valid) { - return; - } + setState(() { _isLoading = true; }); @@ -236,11 +235,9 @@ class _FcsShipmentEditorState extends State { } Future _update() async { + if (!_formKey.currentState!.validate()) return; FcsShipment fcsShipment = _getPayload(); - bool valid = await _validate(fcsShipment); - if (!valid) { - return; - } + setState(() { _isLoading = true; }); diff --git a/lib/pages/staff/staff_pin_editor.dart b/lib/pages/staff/staff_pin_editor.dart index a2a73b6..0f08e9e 100644 --- a/lib/pages/staff/staff_pin_editor.dart +++ b/lib/pages/staff/staff_pin_editor.dart @@ -16,6 +16,7 @@ class StaffPinEditor extends StatefulWidget { } class _StaffPinEditorState extends State { + final _formKey = GlobalKey(); bool _isLoading = false; late User _staff; bool _enablePinLogin = false; @@ -110,28 +111,101 @@ class _StaffPinEditorState extends State { backgroundColor: Colors.white, labelColor: primaryColor, arrowColor: primaryColor), - body: Padding( - padding: const EdgeInsets.only(left: 15.0, right: 15), - child: ListView( - children: [ - Column( - children: [ - Text(_staff.name ?? "", - 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), - newPinBox, - SizedBox(height: 30), - confirmPinBox, - SizedBox(height: 30), - saveButton, - SizedBox(height: 30) - ], + body: Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.only(left: 15.0, right: 15), + child: ListView( + children: [ + Column( + children: [ + Text(_staff.name ?? "", + 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), + FormField( + builder: (FormFieldState state) { + return Column( + 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 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 didn’t match. Try again."; + } + + return null; + }, + ), + SizedBox(height: 20), + saveButton, + SizedBox(height: 30) + ], + ), ), ), )); @@ -150,30 +224,7 @@ class _StaffPinEditorState extends State { } _save() async { - if (_newPin == "") { - 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 didn’t match. Try again."); - return; - } + if (!_formKey.currentState!.validate()) return; setState(() { _isLoading = true; diff --git a/lib/pages/widgets/input_date.dart b/lib/pages/widgets/input_date.dart index 095f822..6024790 100644 --- a/lib/pages/widgets/input_date.dart +++ b/lib/pages/widgets/input_date.dart @@ -84,6 +84,7 @@ class InputDate extends StatelessWidget { hintStyle: TextStyle( height: 1.5, ), + errorStyle: const TextStyle(color: dangerColor, fontSize: 12), labelText: labelTextKey == null ? null : AppTranslations.of(context)!.text(labelTextKey!), @@ -108,6 +109,22 @@ class InputDate extends StatelessWidget { ) : UnderlineInputBorder( 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), ); diff --git a/lib/pages/widgets/input_text.dart b/lib/pages/widgets/input_text.dart index 99d97cc..9d09b07 100644 --- a/lib/pages/widgets/input_text.dart +++ b/lib/pages/widgets/input_text.dart @@ -57,6 +57,7 @@ class InputText extends StatelessWidget { constraints: constraints, contentPadding: contentPadding, // hintText: '', + errorStyle: const TextStyle(color: dangerColor, fontSize: 12), hintStyle: TextStyle( height: 1.5, ), @@ -96,6 +97,22 @@ class InputText extends StatelessWidget { : UnderlineInputBorder( borderSide: BorderSide( 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), );