Merge branch 'master' of tzw/fcs into master

This commit is contained in:
2025-02-10 14:09:19 +06:30
committed by Gogs
13 changed files with 246 additions and 203 deletions

View File

@@ -1,6 +1,8 @@
const uploadPhotoLimit = 10; const uploadPhotoLimit = 10;
const shipmentCountForCartonFilter = 10; const shipmentCountForCartonFilter = 10;
const resendCountSec = 30; const resendCountSec = 30;
int imageQuality = 100;
double imageMaxWidth = 1000;
const config_collection = "configs"; const config_collection = "configs";
const user_collection = "users"; const user_collection = "users";
@@ -84,7 +86,7 @@ const privilege_receiving = "rc";
const privilege_pickup = "pku"; const privilege_pickup = "pku";
const privilege_collect = "col"; const privilege_collect = "col";
const privilege_report = "rpt"; const privilege_report = "rpt";
const privilege_pin ="pin"; const privilege_pin = "pin";
// Pickup types // Pickup types
const shipment_local_pickup = "Local pickup"; const shipment_local_pickup = "Local pickup";

View File

@@ -328,7 +328,8 @@ class _CartonFilterState extends State<CartonFilter> {
onTap: () => _changeStatus(b), onTap: () => _changeStatus(b),
title: Row( title: Row(
children: [ children: [
Text(b, style: const TextStyle(fontSize: 15)), Text(capitalizeFirstLetter(b),
style: const TextStyle(fontSize: 15)),
const SizedBox( const SizedBox(
width: 20, width: 20,
), ),

View File

@@ -31,6 +31,7 @@ class PartSearchDelegate extends SearchDelegate<Carton> {
ThemeData appBarTheme(BuildContext context) { ThemeData appBarTheme(BuildContext context) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
return theme.copyWith( return theme.copyWith(
textSelectionTheme: TextSelectionThemeData(cursorColor: Colors.grey),
appBarTheme: AppBarTheme(color: primaryColor), appBarTheme: AppBarTheme(color: primaryColor),
iconButtonTheme: IconButtonThemeData( iconButtonTheme: IconButtonThemeData(
style: ButtonStyle( style: ButtonStyle(

View File

@@ -24,6 +24,7 @@ class _CartonSizeEditorState extends State<CartonSizeEditor> {
TextEditingController _widthController = new TextEditingController(); TextEditingController _widthController = new TextEditingController();
TextEditingController _heightController = new TextEditingController(); TextEditingController _heightController = new TextEditingController();
TextEditingController _lengthController = new TextEditingController(); TextEditingController _lengthController = new TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _isLoading = false; bool _isLoading = false;
CartonSize? _cartonSize; CartonSize? _cartonSize;
@@ -55,9 +56,16 @@ class _CartonSizeEditorState extends State<CartonSizeEditor> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final nameBox = InputText( final nameBox = InputText(
labelTextKey: 'box.carton_size.name', labelTextKey: 'box.carton_size.name',
iconData: Icons.text_format, iconData: Icons.text_format,
controller: _nameController); controller: _nameController,
validator: (value) {
if (value!.isEmpty) {
return "Enter carton size name";
}
return null;
},
);
final lengthBox = LengthPicker( final lengthBox = LengthPicker(
controller: _lengthController, controller: _lengthController,
@@ -90,10 +98,13 @@ class _CartonSizeEditorState extends State<CartonSizeEditor> {
context, context,
getLocalString(context, 'box.cargo.save.btn'), getLocalString(context, 'box.cargo.save.btn'),
callack: () async { callack: () async {
if (!_formKey.currentState!.validate()) return;
if (_nameController.text == "") { if (_nameController.text == "") {
showMsgDialog(context, "Esrror", "Invalid carton size name!"); showMsgDialog(context, "Error", "Invalid carton size name!");
return; return;
} }
setState(() { setState(() {
_isLoading = true; _isLoading = true;
}); });
@@ -132,35 +143,38 @@ class _CartonSizeEditorState extends State<CartonSizeEditor> {
} }
}, },
); );
return LocalProgress( return Form(
inAsyncCall: _isLoading, key: _formKey,
child: Scaffold( child: LocalProgress(
appBar: LocalAppBar( inAsyncCall: _isLoading,
labelKey: "box.carton_size", child: Scaffold(
backgroundColor: Colors.white, appBar: LocalAppBar(
labelColor: primaryColor, labelKey: "box.carton_size",
arrowColor: primaryColor, backgroundColor: Colors.white,
onBack: () { labelColor: primaryColor,
if (isDataChanged()) { arrowColor: primaryColor,
showConfirmDialog(context, "back.button_confirm", () { onBack: () {
if (isDataChanged()) {
showConfirmDialog(context, "back.button_confirm", () {
Navigator.of(context).pop();
});
} else {
Navigator.of(context).pop(); Navigator.of(context).pop();
}); }
} else { },
Navigator.of(context).pop(); ),
} body: Container(
}, padding: EdgeInsets.all(18),
), child: ListView(
body: Container( shrinkWrap: true,
padding: EdgeInsets.all(18), children: <Widget>[
child: ListView( nameBox,
shrinkWrap: true, dimBox,
children: <Widget>[ SizedBox(height: 40),
nameBox, saveBtn,
dimBox, SizedBox(height: 20),
SizedBox(height: 40), ],
saveBtn, ),
SizedBox(height: 20),
],
), ),
), ),
), ),

View File

@@ -48,7 +48,7 @@ class _FAQEditorState extends State<FAQEditor> {
_mmA.text = _faq.answerMm ?? ''; _mmA.text = _faq.answerMm ?? '';
_pageLabelEng.text = _faq.pageLinkLabelEng ?? ""; _pageLabelEng.text = _faq.pageLinkLabelEng ?? "";
_pageLabelMm.text = _faq.pageLinkLabelMm ?? ""; _pageLabelMm.text = _faq.pageLinkLabelMm ?? "";
_pageLink = _faq.pageLink ?? ''; _pageLink = _faq.pageLink ?? info;
} }
} }

View File

@@ -83,10 +83,15 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
_currentDestination = _currentDestination =
loadingDestination.isNotEmpty ? loadingDestination.first : null; loadingDestination.isNotEmpty ? loadingDestination.first : null;
} else { } else {
_currentShipmentType = model.shipmentTypes.first; _currentShipmentType =
_currentConsignee = model.shipmentConsingees.first; model.shipmentTypes.isNotEmpty ? model.shipmentTypes.first : null;
_currentLoadingPort = model.shipmentPorts.first; _currentConsignee = model.shipmentConsingees.isNotEmpty
_currentDestination = model.shipmentPorts.first; ? model.shipmentConsingees.first
: null;
_currentLoadingPort =
model.shipmentPorts.isNotEmpty ? model.shipmentPorts.first : null;
_currentDestination =
model.shipmentPorts.isNotEmpty ? model.shipmentPorts.first : null;
} }
} }
@@ -357,10 +362,14 @@ class _FcsShipmentEditorState extends State<FcsShipmentEditor> {
return _shipmentNumberController.text != "" || return _shipmentNumberController.text != "" ||
_cutoffDateController.text != "" || _cutoffDateController.text != "" ||
_etaDateController.text != "" || _etaDateController.text != "" ||
_currentConsignee != shipmentModel.shipmentConsingees.first || (shipmentModel.shipmentConsingees.isNotEmpty &&
_currentShipmentType != shipmentModel.shipmentTypes.first || _currentConsignee != shipmentModel.shipmentConsingees.first) ||
_currentLoadingPort != shipmentModel.shipmentPorts.first || (shipmentModel.shipmentTypes.isNotEmpty &&
_currentDestination != shipmentModel.shipmentPorts.first; _currentShipmentType != shipmentModel.shipmentTypes.first) ||
(shipmentModel.shipmentPorts.isNotEmpty &&
_currentLoadingPort != shipmentModel.shipmentPorts.first) ||
(shipmentModel.shipmentPorts.isNotEmpty &&
_currentDestination != shipmentModel.shipmentPorts.first);
} else { } else {
FcsShipment fcsShipment = _getPayload(); FcsShipment fcsShipment = _getPayload();
return widget.shipment!.isChangedForEdit(fcsShipment); return widget.shipment!.isChangedForEdit(fcsShipment);

View File

@@ -404,7 +404,6 @@ String removeTrailingZeros(double number) {
return result; return result;
} }
bool isValidEmail(String email) { bool isValidEmail(String email) {
// Define a regular expression for validating an email // Define a regular expression for validating an email
final emailRegex = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$'); final emailRegex = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$');
@@ -412,3 +411,8 @@ bool isValidEmail(String email) {
// Check if the email matches the pattern // Check if the email matches the pattern
return emailRegex.hasMatch(email); return emailRegex.hasMatch(email);
} }
String capitalizeFirstLetter(String text) {
if (text.isEmpty) return text;
return text[0].toUpperCase() + text.substring(1);
}

View File

@@ -19,6 +19,7 @@ class MarketEditor extends StatefulWidget {
class _MarketEditorState extends State<MarketEditor> { class _MarketEditorState extends State<MarketEditor> {
TextEditingController _marketNameCtl = new TextEditingController(); TextEditingController _marketNameCtl = new TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _isLoading = false; bool _isLoading = false;
List<String> markets = []; List<String> markets = [];
@@ -141,36 +142,46 @@ class _MarketEditorState extends State<MarketEditor> {
await showDialog<String>( await showDialog<String>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return new AlertDialog( return Form(
contentPadding: const EdgeInsets.all(16.0), key: _formKey,
content: new Row( child: new AlertDialog(
children: <Widget>[ contentPadding: const EdgeInsets.all(16.0),
new Expanded( content: new Row(
child: InputText( children: <Widget>[
labelTextKey: "market.edit.name", new Expanded(
controller: _marketNameCtl, child: InputText(
autoFocus: true, labelTextKey: "market.edit.name",
), controller: _marketNameCtl,
) autoFocus: true,
validator: (value) {
if (value!.isEmpty) {
return "Enter market name";
}
return null;
},
),
)
],
),
actions: <Widget>[
new TextButton(
child: LocalText(context, "btn.cancel", color: primaryColor),
onPressed: () {
Navigator.pop(context);
}),
new TextButton(
child: LocalText(
context,
"btn.save",
color: primaryColor,
),
onPressed: () {
if (!_formKey.currentState!.validate()) return;
Navigator.pop(context);
_add();
})
], ],
), ),
actions: <Widget>[
new TextButton(
child: LocalText(context, "btn.cancel", color: primaryColor),
onPressed: () {
Navigator.pop(context);
}),
new TextButton(
child: LocalText(
context,
"btn.save",
color: primaryColor,
),
onPressed: () {
Navigator.pop(context);
_add();
})
],
); );
}, },
); );

View File

@@ -31,20 +31,20 @@ class PackageSearchDelegate extends SearchDelegate<Package> {
ThemeData appBarTheme(BuildContext context) { ThemeData appBarTheme(BuildContext context) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
return theme.copyWith( return theme.copyWith(
appBarTheme: AppBarTheme(color: primaryColor), textSelectionTheme: TextSelectionThemeData(cursorColor: Colors.grey),
iconButtonTheme: IconButtonThemeData( appBarTheme: AppBarTheme(color: primaryColor),
style: ButtonStyle( iconButtonTheme: IconButtonThemeData(
iconColor: WidgetStateProperty.all<Color>(Colors.white))), style: ButtonStyle(
inputDecorationTheme: InputDecorationTheme( iconColor: WidgetStateProperty.all<Color>(Colors.white))),
border: InputBorder.none, inputDecorationTheme: InputDecorationTheme(
hintStyle: TextStyle(color: Colors.grey, fontSize: 14)), border: InputBorder.none,
textTheme: TextTheme( hintStyle: TextStyle(color: Colors.grey, fontSize: 14)),
displayLarge: TextStyle( textTheme: TextTheme(
color: theme.primaryTextTheme.displayLarge?.color, displayLarge: TextStyle(
fontSize: 16, color: theme.primaryTextTheme.displayLarge?.color,
backgroundColor: primaryColor)), fontSize: 16,
primaryColor: primaryColor, backgroundColor: primaryColor)),
); primaryColor: primaryColor);
} }
@override @override
@@ -126,7 +126,7 @@ class PackageSearchDelegate extends SearchDelegate<Package> {
@override @override
Widget buildSuggestions(BuildContext context) { Widget buildSuggestions(BuildContext context) {
return FutureBuilder<List<String>?>( return FutureBuilder<List<String>?>(
future: SharedPref.getRecentSearch('package_search', query), future: SharedPref.getRecentSearch('package_search', query),
builder: (context, snapshot) { builder: (context, snapshot) {
List<String> _oldFilters = snapshot.data ?? []; List<String> _oldFilters = snapshot.data ?? [];
@@ -134,8 +134,7 @@ class PackageSearchDelegate extends SearchDelegate<Package> {
return const Center( return const Center(
child: Opacity( child: Opacity(
opacity: 0.2, opacity: 0.2,
child: Icon(Octicons.package, child: Icon(Octicons.package, color: primaryColor, size: 200),
color: primaryColor, size: 200),
), ),
); );
} }

View File

@@ -34,20 +34,20 @@ class UserSearchDelegate extends SearchDelegate<User> {
ThemeData appBarTheme(BuildContext context) { ThemeData appBarTheme(BuildContext context) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
return theme.copyWith( return theme.copyWith(
appBarTheme: AppBarTheme(color: primaryColor), textSelectionTheme: TextSelectionThemeData(cursorColor: Colors.grey),
iconButtonTheme: IconButtonThemeData( appBarTheme: AppBarTheme(color: primaryColor),
style: ButtonStyle( iconButtonTheme: IconButtonThemeData(
iconColor: WidgetStateProperty.all<Color>(Colors.white))), style: ButtonStyle(
inputDecorationTheme: InputDecorationTheme( iconColor: WidgetStateProperty.all<Color>(Colors.white))),
border: InputBorder.none, inputDecorationTheme: InputDecorationTheme(
hintStyle: TextStyle(color: Colors.grey, fontSize: 14)), border: InputBorder.none,
textTheme: TextTheme( hintStyle: TextStyle(color: Colors.grey, fontSize: 14)),
displayLarge: TextStyle( textTheme: TextTheme(
color: theme.primaryTextTheme.displayLarge?.color, displayLarge: TextStyle(
fontSize: 16, color: theme.primaryTextTheme.displayLarge?.color,
backgroundColor: primaryColor)), fontSize: 16,
primaryColor: primaryColor, backgroundColor: primaryColor)),
); primaryColor: primaryColor);
} }
@override @override

View File

@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import '../../constants.dart';
import 'show_img.dart'; import 'show_img.dart';
typedef OnFile = void Function(File?); typedef OnFile = void Function(File?);
@@ -57,8 +58,8 @@ class _LocalImagePickerState extends State<LocalImagePicker> {
if (camera || gallery) { if (camera || gallery) {
var selectedFile = await ImagePicker().pickImage( var selectedFile = await ImagePicker().pickImage(
source: camera ? ImageSource.camera : ImageSource.gallery, source: camera ? ImageSource.camera : ImageSource.gallery,
imageQuality: 80, imageQuality: imageQuality,
maxWidth: 1000); maxWidth: imageMaxWidth);
if (selectedFile != null) { if (selectedFile != null) {
setState(() { setState(() {
this.file = File(selectedFile.path); this.file = File(selectedFile.path);

View File

@@ -40,96 +40,96 @@ class InputDate extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.only(top: 15.0, bottom: 5), padding: const EdgeInsets.only(top: 15.0, bottom: 5),
child: TextFormField( child: TextFormField(
readOnly: true, readOnly: true,
onTap: () async { onTap: () async {
var dateFormatter = new DateFormat(dateFormatString); var dateFormatter = new DateFormat(dateFormatString);
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
var initialDate = DateTime.now(); var initialDate = DateTime.now();
if (controller.text != "") { if (controller.text != "") {
try { try {
initialDate = dateFormatter.parse(controller.text); initialDate = dateFormatter.parse(controller.text);
} catch (e) {} // ignore error } catch (e) {} // ignore error
} }
var d = await showDatePicker( var d = await showDatePicker(
context: context, context: context,
firstDate: DateTime(0), firstDate: DateTime(0),
lastDate: DateTime(2025), lastDate: DateTime(2050),
initialDate: initialDate, initialDate: initialDate,
builder: (context, child) { builder: (context, child) {
return Theme( return Theme(
data: Theme.of(context).copyWith( data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light( colorScheme: ColorScheme.light(
background: primaryColor, background: primaryColor,
surfaceTint: Colors.white, surfaceTint: Colors.white,
primary: primaryColor), primary: primaryColor),
textButtonTheme: TextButtonThemeData( textButtonTheme: TextButtonThemeData(
style: style: TextButton.styleFrom(foregroundColor: primaryColor),
TextButton.styleFrom(foregroundColor: primaryColor),
),
), ),
child: child!, ),
); child: child!,
}, );
); },
if (d != null) { );
controller.text = dateFormatter.format(d); if (d != null) {
} controller.text = dateFormatter.format(d);
}, }
controller: controller, },
autofocus: autoFocus, controller: controller,
cursorColor: primaryColor, autofocus: autoFocus,
style: textStyle, cursorColor: primaryColor,
maxLines: maxLines, style: textStyle,
keyboardType: textInputType, maxLines: maxLines,
decoration: new InputDecoration( keyboardType: textInputType,
// hintText: '', decoration: new InputDecoration(
hintStyle: TextStyle( // hintText: '',
height: 1.5, hintStyle: TextStyle(
), height: 1.5,
errorStyle: const TextStyle(color: dangerColor, fontSize: 12),
labelText: labelTextKey == null
? null
: AppTranslations.of(context)!.text(labelTextKey!),
labelStyle: languageModel.isEng
? newLabelStyle(color: Colors.black54, fontSize: 20)
: newLabelStyleMM(color: Colors.black54, fontSize: 20),
icon: iconData == null
? null
: Icon(
iconData,
color: primaryColor,
),
enabledBorder: withBorder
? OutlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0),
)
: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: withBorder
? OutlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0),
)
: 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, errorStyle: const TextStyle(color: dangerColor, fontSize: 12),
autovalidateMode: autovalidateMode,), labelText: labelTextKey == null
? null
: AppTranslations.of(context)!.text(labelTextKey!),
labelStyle: languageModel.isEng
? newLabelStyle(color: Colors.black54, fontSize: 20)
: newLabelStyleMM(color: Colors.black54, fontSize: 20),
icon: iconData == null
? null
: Icon(
iconData,
color: primaryColor,
),
enabledBorder: withBorder
? OutlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0),
)
: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
focusedBorder: withBorder
? OutlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0),
)
: 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,
autovalidateMode: autovalidateMode,
),
); );
} }
} }

View File

@@ -11,6 +11,7 @@ import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import '../../constants.dart';
import 'display_image_source.dart'; import 'display_image_source.dart';
import 'multi_img_controller.dart'; import 'multi_img_controller.dart';
@@ -147,11 +148,11 @@ class _MultiImageFileState extends State<MultiImageFile> {
Icon(Icons.error), Icon(Icons.error),
) )
: Image.file( : Image.file(
fileContainers[index].file!, fileContainers[index].file!,
width: 50, width: 50,
height: 50, height: 50,
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
), ),
), ),
widget.enabled widget.enabled
@@ -177,8 +178,8 @@ class _MultiImageFileState extends State<MultiImageFile> {
_openImagePicker(bool camera) async { _openImagePicker(bool camera) async {
var selectedFile = await ImagePicker().pickImage( var selectedFile = await ImagePicker().pickImage(
source: camera ? ImageSource.camera : ImageSource.gallery, source: camera ? ImageSource.camera : ImageSource.gallery,
imageQuality: 80, imageQuality: imageQuality,
maxWidth: 1000); maxWidth: imageMaxWidth);
if (selectedFile != null) { if (selectedFile != null) {
_fileAdded(DisplayImageSource(), File(selectedFile.path)); _fileAdded(DisplayImageSource(), File(selectedFile.path));
} }
@@ -214,8 +215,8 @@ class _MultiImageFileState extends State<MultiImageFile> {
if (camera || gallery) { if (camera || gallery) {
var selectedFile = await ImagePicker().pickImage( var selectedFile = await ImagePicker().pickImage(
source: camera ? ImageSource.camera : ImageSource.gallery, source: camera ? ImageSource.camera : ImageSource.gallery,
imageQuality: 80, imageQuality: imageQuality,
maxWidth: 1000); maxWidth: imageMaxWidth);
if (selectedFile != null) { if (selectedFile != null) {
_fileAdded(fileContainer, _fileAdded(fileContainer,
File.fromRawPath(await selectedFile.readAsBytes())); File.fromRawPath(await selectedFile.readAsBytes()));