Merge branch 'master' of tzw/fcs into master

This commit is contained in:
2024-02-24 00:17:20 +06:30
committed by Gogs
35 changed files with 365 additions and 100 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -358,6 +358,12 @@
"box.delete.btn":"Delete carton",
"box.update_title":"Edit carton",
"box.update.btn":"Update carton",
"box.ship.btn":"Ship this Carton",
"box.arrive.btn":"Arrive this Carton",
"box.invoice.btn":"Invoice this Carton",
"box.ship.confirm":"Confirm ship?",
"box.arrive.confirm":"Confirm arrive?",
"box.invoice.confirm":"Confirm invoice?",
"Boxes End ================================================================":"",
"Delivery Start ================================================================":"",
@@ -400,6 +406,9 @@
"FCSshipment.cancel.confirm":"Cancel this shipment?",
"FCSshipment.carton":"Cartons",
"FCSshipment.package":"Packages",
"FCSshipment.process.confirm":"Confirm process?",
"FCSshipment.arrive.confirm":"Confirm arrive?",
"FCSshipment.invoice.confirm":"Confirm invoice?",
"FCS Shipment End ================================================================":"",
"Shipment Start ================================================================":"",
@@ -458,6 +467,7 @@
"shipment.confirm.complete.confirm":"Complete confirm?",
"shipment.receive.complete.btn":"Complete receive",
"shipment.receive.complete.confirm":"Complete receive?",
"Shipment End ================================================================":"",
"Rate Start ================================================================":"",

View File

@@ -358,6 +358,12 @@
"box.delete.btn":"Delete carton",
"box.update_title":"Edit carton",
"box.update.btn":"Update carton",
"box.ship.btn":"Ship this Carton",
"box.arrive.btn":"Arrive this Carton",
"box.invoice.btn":"Invoice this Carton",
"box.ship.confirm":"ပို့ဆောင်မှုကို အတည်ပြုပါ ?",
"box.arrive.confirm":"ရောက်ရှိကြောင်း အတည်ပြုပါ ?",
"box.invoice.confirm":"ပြေစာ အတည်ပြုပါ ?",
"Boxes End ================================================================":"",
"Delivery Start ================================================================":"",
@@ -403,6 +409,9 @@
"FCSshipment.popupmenu.shipped":"Shipped shipments",
"FCSshipment.carton":"FCS ပုံးများ",
"FCSshipment.package":"FCS အထုပ်",
"FCSshipment.process.confirm":"လုပ်ငန်းစဉ်ကို အတည်ပြုပါ ?",
"FCSshipment.arrive.confirm":"ရောက်ရှိကြောင်း အတည်ပြုပါ ?",
"FCSshipment.invoice.confirm":"ပြေစာအတည်ပြုပါ ?",
"FCS Shipment End ================================================================":"",
"Shipment Start ================================================================":"",
@@ -461,6 +470,7 @@
"shipment.confirm.complete.confirm":"Complete confirm?",
"shipment.receive.complete.btn":"Complete receive",
"shipment.receive.complete.confirm":"Complete receive?",
"Shipment End ================================================================":"",
"Rate Start ================================================================":"",

View File

@@ -12,6 +12,8 @@ import 'package:fcs/helpers/firebase_helper.dart';
import 'package:firebase_auth/firebase_auth.dart' as fb;
import 'package:logging/logging.dart';
import '../services/services.dart';
class AuthFb {
final log = Logger('AuthFb');
@@ -21,6 +23,10 @@ class AuthFb {
late StreamController<User?> controller;
static final fb.FirebaseAuth _fb = fb.FirebaseAuth.instance;
static String _verificationId = '';
static bool _logIn = false;
StreamSubscription<DocumentSnapshot>? userListener;
StreamSubscription<DocumentSnapshot>? userAuthListener;
Future<fcs.AuthResult> sendSmsCodeToPhoneNumber(String phoneNumber) {
Completer<fcs.AuthResult> completer = Completer();
@@ -99,9 +105,13 @@ class AuthFb {
return Future.value(fcs.AuthResult(authStatus: AuthStatus.AUTH_VERIFIED));
}
Future<void> signout() async {
if (userListener != null) await userListener!.cancel();
return _fb.signOut();
Future<void> signoutStart() async {
await userListener?.cancel();
await userAuthListener?.cancel();
}
Future<void> signoutEnd() async {
await _fb.signOut();
}
Future<void> _addUserToStream({bool refreshIdToken = false}) async {
@@ -111,7 +121,6 @@ class AuthFb {
await getClaims(refreshIdToken: refreshIdToken);
log.info("Claims:$claims");
if (claims == null) return;
String? cid = claims["cid"];
User? user;
@@ -123,6 +132,11 @@ class AuthFb {
return;
}
loadUserClaim(claims, user);
controller.add(user);
}
loadUserClaim(Map claims, User user) {
// add privileges
String? privileges = claims["pr"];
if (privileges != null && privileges != "") {
@@ -130,7 +144,6 @@ class AuthFb {
} else {
user.privileges = [];
}
controller.add(user);
}
Future<User?> _getUserFromFirestore(String userID) async {
@@ -205,13 +218,12 @@ class AuthFb {
fb.User? firebaseUser = _fb.currentUser;
if (firebaseUser == null) return null;
Map? claims = await getClaims();
if (claims == null) return null;
String cid = claims["cid"];
return cid;
}
Future<void> _startUserListener() async {
if (userListener != null) userListener!.cancel();
_startAuthListener();
String? _userID = await _getCurrentUserID();
if (_userID == null) {
return;
@@ -221,6 +233,7 @@ class AuthFb {
.collection(user_collection)
.doc(_userID)
.snapshots();
userListener?.cancel();
userListener = snapshot.listen((snap) async {
User user = User.fromMap(snap.data() as Map<String, dynamic>, snap.id);
@@ -229,14 +242,12 @@ class AuthFb {
userListener?.cancel();
return;
}
if (!_logIn) return;
try {
// get privilege from claim
fb.IdTokenResult idToken = await firebaseUser.getIdTokenResult(true);
String? privileges = idToken.claims?["pr"] ?? '';
if (privileges != null && privileges != "") {
user.privileges = privileges.split(":").toList();
}
Map<dynamic, dynamic> claims = await getClaims(refreshIdToken: true);
loadUserClaim(claims, user);
controller.add(user);
} catch (e) {
controller.add(null);
@@ -244,13 +255,47 @@ class AuthFb {
});
}
StreamSubscription<DocumentSnapshot>? userListener;
Future<void> _startAuthListener() async {
String? authId = _fb.currentUser?.uid;
if (authId == null) return;
Stream<DocumentSnapshot> snapshot = FirebaseFirestore.instance
.collection(authCollection)
.doc(authId)
.snapshots();
userAuthListener?.cancel();
userAuthListener = snapshot.listen((snap) async {
if (snap.exists) {
Map<String, dynamic> map = snap.data() as Map<String, dynamic>;
String userID = map['user_id'] ?? "";
User? user = await Services.instance.userService.getUser(userID);
if (user == null) return;
if (_fb.currentUser == null) {
userAuthListener?.cancel();
return;
}
Map<dynamic, dynamic> claims = await getClaims(refreshIdToken: true);
loadUserClaim(claims, user);
log.info("_startAuthListener: $user");
if (_logIn) {
controller.add(user);
}
}
});
}
Stream<User?> user() {
// ignore: close_sinks
StreamSubscription<fb.User?>? authListener;
Future<void> _start() async {
authListener = _fb.authStateChanges().listen((firebaseUser) {
await authListener?.cancel();
authListener = _fb.authStateChanges().listen((firebaseUser) async {
_logIn = firebaseUser != null;
if (firebaseUser == null) {
controller.add(null);
} else {
@@ -261,12 +306,8 @@ class AuthFb {
}
void _stop() {
if (userListener != null) {
userListener!.cancel();
}
if (authListener != null) {
authListener!.cancel();
}
userListener?.cancel();
authListener?.cancel();
}
controller = StreamController<User?>(

View File

@@ -85,4 +85,29 @@ class UserDataProvider {
return await requestAPI("/enable_user", "PUT",
payload: {"id": userID, "enabled": enabled}, token: await getToken());
}
Future<User?> getUser(String userID) async {
if (userID == "") return null;
String path = "/$user_collection";
try {
var snap = await FirebaseFirestore.instance
.collection(path)
.doc(userID)
.get(const GetOptions(source: Source.server));
if (snap.data() == null) return null;
Map<String, dynamic>? data = snap.data() as Map<String, dynamic>;
if (data['delete_time'] == 0) {
User user = User.fromMap(data, snap.id);
return user;
} else {
return null;
}
} catch (e) {
log.warning("Error!! $e");
}
return null;
}
}

View File

@@ -25,11 +25,6 @@ class AuthServiceImp implements AuthService {
return authFb.signInWithPhoneNumber(smsCode);
}
@override
Future<void> signout() {
return authFb.signout();
}
@override
Stream<User?> getUserStream() {
return authFb.user();
@@ -64,4 +59,14 @@ class AuthServiceImp implements AuthService {
Future<void> updatePreferredCurrency(String currency) {
return authFb.updatePreferredCurrency(currency);
}
@override
Future<void> signoutEnd() {
return authFb.signoutEnd();
}
@override
Future<void> signoutStart() {
return authFb.signoutStart();
}
}

View File

@@ -5,7 +5,8 @@ import 'package:fcs/domain/entities/user.dart';
abstract class AuthService {
Future<AuthResult> sendSmsCodeToPhoneNumber(String phoneNumber);
Future<AuthResult> signInWithSmsCode(String smsCode);
Future<void> signout();
Future<void> signoutStart();
Future<void> signoutEnd();
Future<void> signup(String userName);
Future<void> joinInvite(String userName);
Future<void> updateProfileName(String newUserName);

View File

@@ -52,4 +52,9 @@ class UserServiceImp implements UserService {
Future<void> enableUser(String userID, bool enabled) {
return userDataProvider.enableUser(userID, enabled);
}
@override
Future<User?> getUser(String userID) {
return userDataProvider.getUser(userID);
}
}

View File

@@ -9,4 +9,5 @@ abstract class UserService {
Future<void> uploadMsgToken(String token);
Future<void> removeMsgToken(String token);
Future<void> enableUser(String userID, bool enabled);
Future<User?> getUser(String userID);
}

View File

@@ -2,6 +2,7 @@ const uploadPhotoLimit = 10;
const config_collection = "configs";
const user_collection = "users";
const authCollection = "auths";
const invitations_collection = "invitations";
const privilege_collection = "privileges";
const markets_collection = "markets";

View File

@@ -21,7 +21,6 @@ class User {
String? preferCurrency;
bool enablePinLogin;
String? pinDigit;
String? confirmPinDigit;
String get initial =>
name != null && name != "" ? name!.substring(0, 1) : "?";
@@ -60,8 +59,8 @@ class User {
bool get disabled => status != null && status == user_disabled_status;
String get share => "Your phone number:$phoneNumber";
User(
{this.id,
User({
this.id,
this.name,
this.phoneNumber,
this.fcsID,
@@ -74,7 +73,7 @@ class User {
this.preferCurrency,
this.enablePinLogin = false,
this.pinDigit,
this.confirmPinDigit});
});
factory User.fromJson(Map<String, dynamic> json) {
return User(

View File

@@ -15,12 +15,12 @@ Future<String> getToken() async {
return token;
}
Future<Map?> getClaims({bool refreshIdToken = false}) async {
Future<Map> getClaims({bool refreshIdToken = false}) async {
fb.User? firebaseUser = auth.currentUser;
if (firebaseUser == null) return null;
if (firebaseUser == null) return {};
fb.IdTokenResult idToken =
await firebaseUser.getIdTokenResult(refreshIdToken);
return idToken.claims;
return idToken.claims ?? {};
}
// returns list of url

69
lib/helpers/pdf.dart Normal file
View File

@@ -0,0 +1,69 @@
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import '../domain/entities/carton.dart';
Future<void> generateCartonPdf(Carton carton) async {
var myTheme = pw.ThemeData.withFont(
base:
pw.Font.ttf(await rootBundle.load("assets/fonts/OpenSans-Regular.ttf")),
bold: pw.Font.ttf(await rootBundle.load("assets/fonts/OpenSans-Bold.ttf")),
italic:
pw.Font.ttf(await rootBundle.load("assets/fonts/OpenSans-Italic.ttf")),
boldItalic: pw.Font.ttf(
await rootBundle.load("assets/fonts/OpenSans-BoldItalic.ttf")),
);
final doc = pw.Document(
theme: myTheme,
);
PdfPageFormat format =
const PdfPageFormat(PdfPageFormat.inch * 3, PdfPageFormat.inch * 1);
doc.addPage(pw.Page(
pageFormat: format,
build: (pw.Context context) {
return pw.Padding(
padding: const pw.EdgeInsets.all(5),
child: pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.start,
crossAxisAlignment: pw.CrossAxisAlignment.center,
children: <pw.Widget>[
pw.BarcodeWidget(
width: 50,
height: 50,
color: PdfColor.fromHex("#000000"),
barcode: pw.Barcode.qrCode(),
data: carton.cartonNumber!),
pw.Padding(
padding: const pw.EdgeInsets.all(5),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text(carton.cartonNumber!,
style: pw.TextStyle(
fontSize: 12,
)),
pw.Text(carton.userName!,
style: pw.TextStyle(
fontSize: 10,
)),
pw.Text("${carton.actualWeight} lb",
style: pw.TextStyle(
fontSize: 10,
color: PdfColor.fromInt(0xFF757575)))
]),
),
]));
}));
List<int> d = await doc.save();
final path = (await getExternalStorageDirectory())?.path ?? "";
final file = File("$path/${carton.cartonNumber}.pdf");
await file.writeAsBytes(d, flush: true);
OpenFile.open(file.path);
}

View File

@@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;

View File

@@ -4,7 +4,6 @@ import 'package:fcs/pages/widgets/display_text.dart';
import 'package:fcs/pages/widgets/fcs_id_icon.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

View File

@@ -397,8 +397,9 @@ class _CartonInfoState extends State<CartonInfo> {
_cargoTypes.isEmpty ? const SizedBox() : cargosBox,
_surchareItems.isEmpty ? const SizedBox() : surchargeItemBox,
uploadImageBtn,
const SizedBox(height: 30),
img,
const SizedBox(height: 15),
const SizedBox(height: 40),
deleteBtn,
const SizedBox(height: 20)
]))));
@@ -445,7 +446,6 @@ class _CartonInfoState extends State<CartonInfo> {
_init();
}
}
_delete() {
showConfirmDialog(context, "box.delete.confirm", () {
_deleteCarton();

View File

@@ -1,9 +1,12 @@
import 'package:fcs/domain/entities/carton.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/carton/model/carton_model.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 '../../../helpers/pdf.dart';
import '../carton_info.dart';
class CartonListRow extends StatelessWidget {
@@ -64,7 +67,11 @@ class CartonListRow extends StatelessWidget {
),
const SizedBox(width: 15),
IconButton(
onPressed: () {}, icon: Icon(AntDesign.qrcode,color: Colors.black))
onPressed: () {
_pdf(box, context);
},
icon:
Icon(AntDesign.qrcode, color: Colors.black))
],
),
),
@@ -100,4 +107,11 @@ class CartonListRow extends StatelessWidget {
),
);
}
_pdf(Carton carton, BuildContext context) async {
Carton? c = await context.read<CartonModel>().getCarton(carton.id!);
if (c == null) return;
generateCartonPdf(c);
}
}

View File

@@ -77,7 +77,7 @@ class _CustomerListState extends State<CustomerList> {
children: <Widget>[
Expanded(
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: new Row(
children: <Widget>[
InkWell(

View File

@@ -1,5 +1,3 @@
import 'dart:ui';
import 'package:country_code_picker/country_code_picker.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/customer/model/customer_model.dart';

View File

@@ -135,21 +135,21 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
padding: const EdgeInsets.symmetric(horizontal: 30),
child: LocalButton(
textKey: "FCSshipment.process.btn",
callBack: () {},
callBack: _process,
),
);
final arriveBtn = Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: LocalButton(
textKey: "FCSshipment.arrive.btn",
callBack: () {},
callBack: _arrive,
),
);
final invoiceBtn = Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: LocalButton(
textKey: "FCSshipment.invoice.btn",
callBack: () {},
callBack: _invoice,
),
);
@@ -281,6 +281,30 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
});
}
_process() {
showConfirmDialog(context, "FCSshipment.process.confirm", () {
_processFcsShipment();
});
}
_processFcsShipment() async {
setState(() {
_isLoading = true;
});
try {
FcsShipmentModel fcsShipmentModel =
Provider.of<FcsShipmentModel>(context, listen: false);
await fcsShipmentModel.process(_fcsShipment!.id!);
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_ship() {
showConfirmDialog(context, "FCSshipment.ship.confirm", () {
_shipFcsShipment();
@@ -305,6 +329,54 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
}
}
_arrive() {
showConfirmDialog(context, "FCSshipment.arrive.confirm", () {
_arriveFcsShipment();
});
}
_arriveFcsShipment() async {
setState(() {
_isLoading = true;
});
try {
FcsShipmentModel fcsShipmentModel =
Provider.of<FcsShipmentModel>(context, listen: false);
await fcsShipmentModel.arrive(_fcsShipment!.id!);
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_invoice() {
showConfirmDialog(context, "FCSshipment.invoice.confirm", () {
_invoiceFcsShipment();
});
}
_invoiceFcsShipment() async {
setState(() {
_isLoading = true;
});
try {
FcsShipmentModel fcsShipmentModel =
Provider.of<FcsShipmentModel>(context, listen: false);
await fcsShipmentModel.invoice(_fcsShipment!.id!);
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_showPDF(int id) async {
setState(() {
_isLoading = true;

View File

@@ -2,7 +2,6 @@ import 'package:fcs/domain/entities/shipment.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
typedef OnAdd(Shipment shipment);

View File

@@ -10,7 +10,7 @@ abstract class BaseModel extends ChangeNotifier {
Setting? setting;
MainModel? mainModel;
void initUser(User user) async {
void initUser(User? user) async {
this.user = user;
}

View File

@@ -21,7 +21,7 @@ class MainModel extends ChangeNotifier {
set setMessaginToken(token) {
this.messagingToken = token;
uploadMsgToken();
_uploadMsgToken();
}
Setting? setting;
@@ -84,24 +84,33 @@ class MainModel extends ChangeNotifier {
this.isFirstLaunch = await SharedPref.isFirstLaunch() ?? true;
this.packageInfo = await PackageInfo.fromPlatform();
if (userListener != null) userListener!.cancel();
userListener?.cancel();
userListener =
Services.instance.authService.getUserStream().listen((_user) {
bool isFirstTime = user == null && _user != null;
bool diffPrivilege =
_user != null && (user == null || user!.diffPrivileges(_user));
bool loggingOut = user != null && _user == null;
user = _user;
if (_user != null) {
models.forEach((m) => m.initUser(_user));
// call diffPrivileges if privilege changed or first time login
if (this.user == null || _user.diffPrivileges(this.user!)) {
models.forEach((m) => m.privilegeChanged());
}
if (this.user == null) {
uploadMsgToken();
}
} else {
if (this.user != null) {
models.forEach((m) => m.logout());
for (final m in models) {
m.initUser(_user);
if (diffPrivilege) {
m.privilegeChanged();
}
}
this.user = _user;
}
if (loggingOut) {
for (final m in models) {
m.logout();
}
}
if (isFirstTime) {
_uploadMsgToken();
}
isLoaded = true;
notifyListeners();
});
@@ -136,22 +145,29 @@ class MainModel extends ChangeNotifier {
return authResult;
}
Future<void>? uploadMsgToken() {
if (messagingToken == null || user == null) return null;
Future<void> _uploadMsgToken() {
if (messagingToken == null || user == null) return Future.value();
return Services.instance.userService.uploadMsgToken(messagingToken!);
}
Future<void>? removeMsgToken() {
if (messagingToken == null || user == null) return null;
Future<void> _removeMsgToken() {
if (messagingToken == null || user == null) return Future.value();
return Services.instance.userService.removeMsgToken(messagingToken!);
}
Future<void> signout() async {
try {
await removeMsgToken();
} catch (e) {}
await Services.instance.authService.signout();
models.forEach((m) => m.logout());
await Services.instance.authService.signoutStart();
await _removeMsgToken();
for (var i = 0; i < models.length; i++) {
models[i].initUser(null);
models[i].logout();
}
await Services.instance.authService.signoutEnd();
} catch (e) {
log.info("signout:${e.toString()}");
}
}
Future<bool> hasInvite() async {

View File

@@ -10,7 +10,6 @@ import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/local_dropdown.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart';
import 'package:provider/provider.dart';

View File

@@ -44,10 +44,11 @@ class _PinLoginPageState extends State<PinLoginPage> {
cursorColor: primaryColor,
keyboardType: TextInputType.text,
decoration: new InputDecoration(
prefix: Text(
prefixIcon: Text(
prefixText,
style: TextStyle(color: Colors.black),
),
prefixIconConstraints: BoxConstraints(minWidth: 0, minHeight: 0),
contentPadding: EdgeInsets.all(0),
labelStyle: newLabelStyle(color: Colors.black54, fontSize: 17),
enabledBorder: UnderlineInputBorder(

View File

@@ -81,7 +81,7 @@ class StaffModel extends BaseModel {
Future<void> updatePin(
{required String userID,
required bool enablePin,
required int pin}) async {
required int? pin}) async {
// await request("/employee/pin", "PUT",
// payload: {
// "id": userID,

View File

@@ -39,7 +39,7 @@ class _StaffPinEditorState extends State<StaffPinEditor> {
_staff = widget.staff;
_enablePinLogin = _staff.enablePinLogin;
_newPin = _staff.pinDigit ?? "";
_confirmPin = _staff.confirmPinDigit ?? "";
_confirmPin = _staff.pinDigit ?? "";
_newPinCtl.text = _newPin;
_confirmPinCtl.text = _confirmPin;
_checkFocusNode();
@@ -298,7 +298,9 @@ class _StaffPinEditorState extends State<StaffPinEditor> {
}
_save() async {
if (_enablePinLogin) {
if (!_formKey.currentState!.validate()) return;
}
setState(() {
_isLoading = true;
@@ -308,7 +310,7 @@ class _StaffPinEditorState extends State<StaffPinEditor> {
await context.read<StaffModel>().updatePin(
userID: _staff.id!,
enablePin: _enablePinLogin,
pin: int.parse(_confirmPin));
pin: _enablePinLogin ? int.parse(_confirmPin) : null);
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());

View File

@@ -1,5 +1,4 @@
import 'package:fcs/helpers/theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'callbacks.dart';

View File

@@ -1,6 +1,5 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'show_img.dart';

View File

@@ -1,7 +1,6 @@
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/main/model/language_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';

View File

@@ -1,7 +1,6 @@
import 'dart:io';
import 'package:fcs/helpers/theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';

View File

@@ -54,6 +54,8 @@ dependencies:
cached_network_image: ^3.3.1
flutter_cache_manager: ^3.1.2
flutter_vector_icons: ^2.0.0
open_file: ^3.3.2
pdf: ^3.10.8
dev_dependencies:
flutter_test:
@@ -65,6 +67,7 @@ flutter:
assets:
- assets/
- assets/local/
- assets/fonts/
fonts:
- family: Roboto