diff --git a/assets/fonts/OpenSans-Bold.ttf b/assets/fonts/OpenSans-Bold.ttf new file mode 100644 index 0000000..4d8f3e5 Binary files /dev/null and b/assets/fonts/OpenSans-Bold.ttf differ diff --git a/assets/fonts/OpenSans-BoldItalic.ttf b/assets/fonts/OpenSans-BoldItalic.ttf new file mode 100644 index 0000000..eba75d7 Binary files /dev/null and b/assets/fonts/OpenSans-BoldItalic.ttf differ diff --git a/assets/fonts/OpenSans-Italic.ttf b/assets/fonts/OpenSans-Italic.ttf new file mode 100644 index 0000000..b088474 Binary files /dev/null and b/assets/fonts/OpenSans-Italic.ttf differ diff --git a/assets/fonts/OpenSans-Regular.ttf b/assets/fonts/OpenSans-Regular.ttf new file mode 100644 index 0000000..3a29f26 Binary files /dev/null and b/assets/fonts/OpenSans-Regular.ttf differ diff --git a/assets/local/localization_en.json b/assets/local/localization_en.json index e531f33..ee16197 100644 --- a/assets/local/localization_en.json +++ b/assets/local/localization_en.json @@ -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 ================================================================":"", diff --git a/assets/local/localization_mu.json b/assets/local/localization_mu.json index 6e6424a..d088e21 100644 --- a/assets/local/localization_mu.json +++ b/assets/local/localization_mu.json @@ -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 ================================================================":"", diff --git a/lib/data/provider/auth_fb.dart b/lib/data/provider/auth_fb.dart index d89e12d..7e79a04 100644 --- a/lib/data/provider/auth_fb.dart +++ b/lib/data/provider/auth_fb.dart @@ -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 controller; static final fb.FirebaseAuth _fb = fb.FirebaseAuth.instance; static String _verificationId = ''; + static bool _logIn = false; + + StreamSubscription? userListener; + StreamSubscription? userAuthListener; Future sendSmsCodeToPhoneNumber(String phoneNumber) { Completer completer = Completer(); @@ -99,9 +105,13 @@ class AuthFb { return Future.value(fcs.AuthResult(authStatus: AuthStatus.AUTH_VERIFIED)); } - Future signout() async { - if (userListener != null) await userListener!.cancel(); - return _fb.signOut(); + Future signoutStart() async { + await userListener?.cancel(); + await userAuthListener?.cancel(); + } + + Future signoutEnd() async { + await _fb.signOut(); } Future _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 _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 _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, 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 claims = await getClaims(refreshIdToken: true); + loadUserClaim(claims, user); controller.add(user); } catch (e) { controller.add(null); @@ -244,13 +255,47 @@ class AuthFb { }); } - StreamSubscription? userListener; + Future _startAuthListener() async { + String? authId = _fb.currentUser?.uid; + if (authId == null) return; + + Stream snapshot = FirebaseFirestore.instance + .collection(authCollection) + .doc(authId) + .snapshots(); + userAuthListener?.cancel(); + userAuthListener = snapshot.listen((snap) async { + if (snap.exists) { + Map map = snap.data() as Map; + 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 claims = await getClaims(refreshIdToken: true); + loadUserClaim(claims, user); + + log.info("_startAuthListener: $user"); + if (_logIn) { + controller.add(user); + } + } + }); + } + Stream user() { // ignore: close_sinks StreamSubscription? authListener; Future _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( diff --git a/lib/data/provider/user_data_provider.dart b/lib/data/provider/user_data_provider.dart index 3a606de..1756eab 100644 --- a/lib/data/provider/user_data_provider.dart +++ b/lib/data/provider/user_data_provider.dart @@ -85,4 +85,29 @@ class UserDataProvider { return await requestAPI("/enable_user", "PUT", payload: {"id": userID, "enabled": enabled}, token: await getToken()); } + + Future 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? data = snap.data() as Map; + + 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; + } } diff --git a/lib/data/services/auth_imp.dart b/lib/data/services/auth_imp.dart index 056907f..e3d1b11 100644 --- a/lib/data/services/auth_imp.dart +++ b/lib/data/services/auth_imp.dart @@ -25,11 +25,6 @@ class AuthServiceImp implements AuthService { return authFb.signInWithPhoneNumber(smsCode); } - @override - Future signout() { - return authFb.signout(); - } - @override Stream getUserStream() { return authFb.user(); @@ -64,4 +59,14 @@ class AuthServiceImp implements AuthService { Future updatePreferredCurrency(String currency) { return authFb.updatePreferredCurrency(currency); } + + @override + Future signoutEnd() { + return authFb.signoutEnd(); + } + + @override + Future signoutStart() { + return authFb.signoutStart(); + } } diff --git a/lib/data/services/auth_service.dart b/lib/data/services/auth_service.dart index a276495..ceba0bf 100644 --- a/lib/data/services/auth_service.dart +++ b/lib/data/services/auth_service.dart @@ -5,7 +5,8 @@ import 'package:fcs/domain/entities/user.dart'; abstract class AuthService { Future sendSmsCodeToPhoneNumber(String phoneNumber); Future signInWithSmsCode(String smsCode); - Future signout(); + Future signoutStart(); + Future signoutEnd(); Future signup(String userName); Future joinInvite(String userName); Future updateProfileName(String newUserName); diff --git a/lib/data/services/user_imp.dart b/lib/data/services/user_imp.dart index 9b5305a..f6e2baa 100644 --- a/lib/data/services/user_imp.dart +++ b/lib/data/services/user_imp.dart @@ -52,4 +52,9 @@ class UserServiceImp implements UserService { Future enableUser(String userID, bool enabled) { return userDataProvider.enableUser(userID, enabled); } + + @override + Future getUser(String userID) { + return userDataProvider.getUser(userID); + } } diff --git a/lib/data/services/user_service.dart b/lib/data/services/user_service.dart index 0dd4297..1e929a9 100644 --- a/lib/data/services/user_service.dart +++ b/lib/data/services/user_service.dart @@ -9,4 +9,5 @@ abstract class UserService { Future uploadMsgToken(String token); Future removeMsgToken(String token); Future enableUser(String userID, bool enabled); + Future getUser(String userID); } diff --git a/lib/domain/constants.dart b/lib/domain/constants.dart index 1bf7bc6..52f6bfb 100644 --- a/lib/domain/constants.dart +++ b/lib/domain/constants.dart @@ -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"; @@ -113,8 +114,8 @@ const carton_processing_status = "processing"; const carton_arrived_status = "arrived"; const carton_invoiced_status = "invoiced"; const carton_canceled_status = "canceled"; -const all_status= "All stauts"; -const all ="All"; +const all_status = "All stauts"; +const all = "All"; // shipment status const shipment_pending_status = "pending"; @@ -141,6 +142,6 @@ const payment_canceled_status = "canceled"; const delivery_caton = "Delivery carton"; const pickup_carton = "Pick-up carton"; -// bill -const billToSender ="Bill to sender"; -const billToConsignee="Bill to consignee"; +// bill +const billToSender = "Bill to sender"; +const billToConsignee = "Bill to consignee"; diff --git a/lib/domain/entities/user.dart b/lib/domain/entities/user.dart index e0c7826..8e1a73f 100644 --- a/lib/domain/entities/user.dart +++ b/lib/domain/entities/user.dart @@ -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,21 +59,21 @@ class User { bool get disabled => status != null && status == user_disabled_status; String get share => "Your phone number:$phoneNumber"; - User( - {this.id, - this.name, - this.phoneNumber, - this.fcsID, - this.status, - this.privileges = const [], - this.lastMessage, - this.lastMessageTime, - this.userUnseenCount = 0, - this.fcsUnseenCount = 0, - this.preferCurrency, - this.enablePinLogin = false, - this.pinDigit, - this.confirmPinDigit}); + User({ + this.id, + this.name, + this.phoneNumber, + this.fcsID, + this.status, + this.privileges = const [], + this.lastMessage, + this.lastMessageTime, + this.userUnseenCount = 0, + this.fcsUnseenCount = 0, + this.preferCurrency, + this.enablePinLogin = false, + this.pinDigit, + }); factory User.fromJson(Map json) { return User( diff --git a/lib/helpers/firebase_helper.dart b/lib/helpers/firebase_helper.dart index 4efa642..3c8b1c8 100644 --- a/lib/helpers/firebase_helper.dart +++ b/lib/helpers/firebase_helper.dart @@ -15,12 +15,12 @@ Future getToken() async { return token; } -Future getClaims({bool refreshIdToken = false}) async { +Future 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 diff --git a/lib/helpers/pdf.dart b/lib/helpers/pdf.dart new file mode 100644 index 0000000..20d7159 --- /dev/null +++ b/lib/helpers/pdf.dart @@ -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 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.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 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); +} diff --git a/lib/localization/app_translations.dart b/lib/localization/app_translations.dart index 73242b2..ade1cc8 100644 --- a/lib/localization/app_translations.dart +++ b/lib/localization/app_translations.dart @@ -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; diff --git a/lib/pages/buying_instruction/buying_online.dart b/lib/pages/buying_instruction/buying_online.dart index ceb8b74..3cba994 100644 --- a/lib/pages/buying_instruction/buying_online.dart +++ b/lib/pages/buying_instruction/buying_online.dart @@ -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'; diff --git a/lib/pages/carton/carton_info.dart b/lib/pages/carton/carton_info.dart index 8f125f2..7ae1d9f 100644 --- a/lib/pages/carton/carton_info.dart +++ b/lib/pages/carton/carton_info.dart @@ -315,8 +315,8 @@ class _CartonInfoState extends State { child: LocalText(context, "box.imageupload.title", color: Colors.white, fontSize: 14)), ); - - final deleteBtn = Padding( + + final deleteBtn = Padding( padding: const EdgeInsets.symmetric(horizontal: 30), child: LocalButton( color: dangerColor, @@ -397,9 +397,10 @@ class _CartonInfoState extends State { _cargoTypes.isEmpty ? const SizedBox() : cargosBox, _surchareItems.isEmpty ? const SizedBox() : surchargeItemBox, uploadImageBtn, + const SizedBox(height: 30), img, - const SizedBox(height: 15), - deleteBtn, + const SizedBox(height: 40), + deleteBtn, const SizedBox(height: 20) ])))); } @@ -445,8 +446,7 @@ class _CartonInfoState extends State { _init(); } } - - _delete() { + _delete() { showConfirmDialog(context, "box.delete.confirm", () { _deleteCarton(); }); diff --git a/lib/pages/carton/widget/carton_list_row.dart b/lib/pages/carton/widget/carton_list_row.dart index c69dfca..f69ce2f 100644 --- a/lib/pages/carton/widget/carton_list_row.dart +++ b/lib/pages/carton/widget/carton_list_row.dart @@ -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().getCarton(carton.id!); + if (c == null) return; + + generateCartonPdf(c); + } } diff --git a/lib/pages/customer/customer_list.dart b/lib/pages/customer/customer_list.dart index 6225afc..eef9255 100644 --- a/lib/pages/customer/customer_list.dart +++ b/lib/pages/customer/customer_list.dart @@ -77,7 +77,7 @@ class _CustomerListState extends State { children: [ Expanded( child: new Padding( - padding: const EdgeInsets.symmetric(vertical: 5.0), + padding: const EdgeInsets.symmetric(vertical: 8.0), child: new Row( children: [ InkWell( diff --git a/lib/pages/customer/invitation_create.dart b/lib/pages/customer/invitation_create.dart index ac40275..a148e81 100644 --- a/lib/pages/customer/invitation_create.dart +++ b/lib/pages/customer/invitation_create.dart @@ -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'; diff --git a/lib/pages/fcs_shipment/fcs_shipment_info.dart b/lib/pages/fcs_shipment/fcs_shipment_info.dart index 13effdd..c622ee8 100644 --- a/lib/pages/fcs_shipment/fcs_shipment_info.dart +++ b/lib/pages/fcs_shipment/fcs_shipment_info.dart @@ -135,21 +135,21 @@ class _FcsShipmentInfoState extends State { 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 { }); } + _process() { + showConfirmDialog(context, "FCSshipment.process.confirm", () { + _processFcsShipment(); + }); + } + + _processFcsShipment() async { + setState(() { + _isLoading = true; + }); + try { + FcsShipmentModel fcsShipmentModel = + Provider.of(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 { } } + _arrive() { + showConfirmDialog(context, "FCSshipment.arrive.confirm", () { + _arriveFcsShipment(); + }); + } + + _arriveFcsShipment() async { + setState(() { + _isLoading = true; + }); + try { + FcsShipmentModel fcsShipmentModel = + Provider.of(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(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; diff --git a/lib/pages/invoice/editor/invoice_handling_fee_list.dart b/lib/pages/invoice/editor/invoice_handling_fee_list.dart index e2fbd41..269c57a 100644 --- a/lib/pages/invoice/editor/invoice_handling_fee_list.dart +++ b/lib/pages/invoice/editor/invoice_handling_fee_list.dart @@ -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); diff --git a/lib/pages/main/model/base_model.dart b/lib/pages/main/model/base_model.dart index a5cbc4f..77d94fc 100644 --- a/lib/pages/main/model/base_model.dart +++ b/lib/pages/main/model/base_model.dart @@ -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; } diff --git a/lib/pages/main/model/main_model.dart b/lib/pages/main/model/main_model.dart index 67a963b..c76dc0f 100644 --- a/lib/pages/main/model/main_model.dart +++ b/lib/pages/main/model/main_model.dart @@ -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? uploadMsgToken() { - if (messagingToken == null || user == null) return null; + Future _uploadMsgToken() { + if (messagingToken == null || user == null) return Future.value(); return Services.instance.userService.uploadMsgToken(messagingToken!); } - Future? removeMsgToken() { - if (messagingToken == null || user == null) return null; + Future _removeMsgToken() { + if (messagingToken == null || user == null) return Future.value(); return Services.instance.userService.removeMsgToken(messagingToken!); } Future 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 hasInvite() async { diff --git a/lib/pages/rates/shipment_rates_calculate.dart b/lib/pages/rates/shipment_rates_calculate.dart index 949a2cc..9fea9ce 100644 --- a/lib/pages/rates/shipment_rates_calculate.dart +++ b/lib/pages/rates/shipment_rates_calculate.dart @@ -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'; diff --git a/lib/pages/signin/pinlogin_page.dart b/lib/pages/signin/pinlogin_page.dart index 9c39f39..14d9eb6 100644 --- a/lib/pages/signin/pinlogin_page.dart +++ b/lib/pages/signin/pinlogin_page.dart @@ -44,10 +44,11 @@ class _PinLoginPageState extends State { 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( diff --git a/lib/pages/staff/model/staff_model.dart b/lib/pages/staff/model/staff_model.dart index 0db5bfe..58fdb6f 100644 --- a/lib/pages/staff/model/staff_model.dart +++ b/lib/pages/staff/model/staff_model.dart @@ -81,7 +81,7 @@ class StaffModel extends BaseModel { Future updatePin( {required String userID, required bool enablePin, - required int pin}) async { + required int? pin}) async { // await request("/employee/pin", "PUT", // payload: { // "id": userID, diff --git a/lib/pages/staff/staff_pin_editor.dart b/lib/pages/staff/staff_pin_editor.dart index ab2ceef..7b686e7 100644 --- a/lib/pages/staff/staff_pin_editor.dart +++ b/lib/pages/staff/staff_pin_editor.dart @@ -39,7 +39,7 @@ class _StaffPinEditorState extends State { _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 { } _save() async { - if (!_formKey.currentState!.validate()) return; + if (_enablePinLogin) { + if (!_formKey.currentState!.validate()) return; + } setState(() { _isLoading = true; @@ -308,7 +310,7 @@ class _StaffPinEditorState extends State { await context.read().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()); diff --git a/lib/pages/widgets/fcs_expansion_tile.dart b/lib/pages/widgets/fcs_expansion_tile.dart index 633e305..e38e65c 100644 --- a/lib/pages/widgets/fcs_expansion_tile.dart +++ b/lib/pages/widgets/fcs_expansion_tile.dart @@ -1,5 +1,4 @@ import 'package:fcs/helpers/theme.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'callbacks.dart'; diff --git a/lib/pages/widgets/img_url.dart b/lib/pages/widgets/img_url.dart index 6cdb69e..68f4fc8 100644 --- a/lib/pages/widgets/img_url.dart +++ b/lib/pages/widgets/img_url.dart @@ -1,6 +1,5 @@ import 'dart:io'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'show_img.dart'; diff --git a/lib/pages/widgets/input_time.dart b/lib/pages/widgets/input_time.dart index 8c283a1..6f69ff6 100644 --- a/lib/pages/widgets/input_time.dart +++ b/lib/pages/widgets/input_time.dart @@ -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'; diff --git a/lib/pages/widgets/show_img.dart b/lib/pages/widgets/show_img.dart index 191096a..a3bdb21 100644 --- a/lib/pages/widgets/show_img.dart +++ b/lib/pages/widgets/show_img.dart @@ -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'; diff --git a/pubspec.yaml b/pubspec.yaml index 6f2c415..c53d646 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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