From eccea7ee02061882a2fcc304d349c13ba5d16618 Mon Sep 17 00:00:00 2001 From: Sai Naw Wun Date: Sun, 13 Sep 2020 21:49:39 +0630 Subject: [PATCH] add customers and staffs --- assets/local/localization_en.json | 60 ++- assets/local/localization_mu.json | 60 +-- lib/app.dart | 21 +- lib/fcs/common/data/providers/auth_fb.dart | 71 ++- .../data/providers/user_data_provider.dart | 38 ++ .../data/providers/user_fb_data_provider.dart | 7 - lib/fcs/common/domain/constants.dart | 12 +- lib/fcs/common/domain/entities/role.dart | 7 +- lib/fcs/common/domain/entities/setting.dart | 6 + lib/fcs/common/domain/entities/user.dart | 50 ++- lib/fcs/common/helpers/const.dart | 3 + .../pages/customers/customer_editor.dart | 407 ++++-------------- .../common/pages/customers/customer_list.dart | 210 +++++---- .../pages/customers/invitation_detail.dart | 159 +++++++ .../pages/customers/invitation_editor.dart | 105 +++++ .../pages/customers/invitation_list.dart | 148 +++++++ .../pages/customers/invitation_page.dart | 67 --- .../pages/customers/model/customer_model.dart | 103 +++-- .../faq/{faq_page.dart => faq_list_page.dart} | 38 +- lib/fcs/common/pages/home_page.dart | 54 ++- .../pages/initial_language_selection.dart | 239 +++++----- lib/fcs/common/pages/model/main_model.dart | 36 +- .../common/pages/profile/profile_edit.dart | 106 +++++ .../pages/{ => profile}/profile_page.dart | 287 ++++++------ .../pages/signin/invitation_request_page.dart | 7 +- .../pages/signin/model/signin_model.dart | 22 - lib/fcs/common/pages/signin/signin_logic.dart | 31 ++ lib/fcs/common/pages/signin/signin_page.dart | 9 +- lib/fcs/common/pages/signin/signup_page.dart | 16 +- .../common/pages/signin/sms_code_page.dart | 21 +- lib/fcs/common/pages/splash_page.dart | 2 +- .../common/pages/staff/model/staff_model.dart | 88 ++++ lib/fcs/common/pages/staff/staff_editor.dart | 276 ++++++++++++ lib/fcs/common/pages/staff/staff_list.dart | 124 ++++++ lib/fcs/common/pages/util.dart | 42 +- lib/fcs/common/pages/welcome_page.dart | 2 +- .../common/pages/widgets/display_text.dart | 69 +++ lib/fcs/common/services/auth_imp.dart | 14 +- lib/fcs/common/services/auth_service.dart | 2 + lib/fcs/common/services/services.dart | 8 +- lib/fcs/common/services/user_imp.dart | 43 +- lib/fcs/common/services/user_interface.dart | 5 - lib/fcs/common/services/user_service.dart | 8 + pubspec.lock | 37 +- pubspec.yaml | 1 + 45 files changed, 2094 insertions(+), 1027 deletions(-) create mode 100644 lib/fcs/common/data/providers/user_data_provider.dart delete mode 100644 lib/fcs/common/data/providers/user_fb_data_provider.dart create mode 100644 lib/fcs/common/helpers/const.dart create mode 100644 lib/fcs/common/pages/customers/invitation_detail.dart create mode 100644 lib/fcs/common/pages/customers/invitation_editor.dart create mode 100644 lib/fcs/common/pages/customers/invitation_list.dart delete mode 100644 lib/fcs/common/pages/customers/invitation_page.dart rename lib/fcs/common/pages/faq/{faq_page.dart => faq_list_page.dart} (78%) create mode 100644 lib/fcs/common/pages/profile/profile_edit.dart rename lib/fcs/common/pages/{ => profile}/profile_page.dart (52%) delete mode 100644 lib/fcs/common/pages/signin/model/signin_model.dart create mode 100644 lib/fcs/common/pages/signin/signin_logic.dart create mode 100644 lib/fcs/common/pages/staff/model/staff_model.dart create mode 100644 lib/fcs/common/pages/staff/staff_editor.dart create mode 100644 lib/fcs/common/pages/staff/staff_list.dart create mode 100644 lib/fcs/common/pages/widgets/display_text.dart delete mode 100644 lib/fcs/common/services/user_interface.dart create mode 100644 lib/fcs/common/services/user_service.dart diff --git a/assets/local/localization_en.json b/assets/local/localization_en.json index a3e3f54..37990f5 100644 --- a/assets/local/localization_en.json +++ b/assets/local/localization_en.json @@ -48,7 +48,9 @@ "language.selection.title":"Please select your language", "welcome.signin":"Sign In", - "welcome.msg":"Welcome to FCS Logistics!", + "welcome.msg":"Welcome to FCS!", + + "home.invitation.request.msg":"We are working on your invitation request!", "sms.verify.title":"Verify your number", "sms.six.digit":"Enter 6 digit sms code sent to", @@ -60,10 +62,44 @@ "invite.request":"Request Invitation", "invite.request.successful":"Successfully requested!", - "================================================================":"", + "customer.list.title":"Customers", + "customer.name":"Name", + "customer.phone":"Phone Number", + "customer.status":"Status", + "customer.fcs.id":"FCS ID", + "customer.invitation.request.confirm":"Accept Customer", + + "invitation.list": "Invitations", + "invitation.edit": "Invitation", + "invitation.new": "New Invitation", + "invitation.confirm.delete": "Delete this invitation?", + + "staff.title":"Staffs", + "staff.list.title":"Staffs", + "staff.new":"New Staff", + "staff.form.title":"Staff", + "staff.add":"Add", + "staff.update":"Update", + + "profile.title": "My Profile", + "profile.edit_title": "Edit My Profile", + "profile.name": "Name", + "profile.phone": "Phone", + "profile.language": "Languages", + "profile.logout": "logout", + "profile.usa.shipping.address": "USA Shipping Address", + "profile.logout.confirm":"Are you sure want to logout?", + "profile.devices":"Devices", + "profile.email":"Email", + "profile.privilege":"Privilege", + "btn.save": "Save", "btn.approve":"Approve", + "btn.delete":"Delete", + + "================================================================":"", + "product": "Product", "price": "Price", @@ -167,16 +203,7 @@ "reg.confirm":"Submit Registration?", "reg.date":"Registeration Date", - "profile.title": "Profile", - "profile.edit_title": "Edit FCS Profile", - "profile.name": "Name", - "profile.phone": "Phone", - "profile.language": "Languages", - "profile.logout": "logout", - "profile.logout.confirm":"Are you sure want to logout?", - "profile.devices":"Devices", - "profile.email":"Email", - "profile.privilege":"Privilege", + "device.confirm":"Confirm this device?", "device.logout":"Logout this device?", @@ -530,12 +557,6 @@ "notifications.title":"Notification", - "staff.title":"Staffs", - "staff.list.title":"STAFFS", - "staff.new":"New Staff", - "staff.form.title":"STAFF", - "staff.add":"Add", - "staff.update":"Update", "shipment.title":"FCS Shipments", "shipment.list.title":"FCS SHIPMENTS", @@ -605,9 +626,6 @@ "invoice.add_package":"Add Package", - "customer.list.title":"CUSTOMERS", - "customer.form.title":"CUSTOMER", - "customer.invite":"Invite", "fcs.btn": "FCS Profile", "fcs.profile": "FCS PROFILE", diff --git a/assets/local/localization_mu.json b/assets/local/localization_mu.json index 9167a94..57ec0f8 100644 --- a/assets/local/localization_mu.json +++ b/assets/local/localization_mu.json @@ -48,7 +48,9 @@ "language.selection.title":"ဘာသာစကား ရွေးချယ်ပါ", "welcome.signin":"ဝင်မည်", - "welcome.msg":"FCS Logistics က ကြိုဆိုပါတယ်!", + "welcome.msg":"FCS က ကြိုဆိုပါတယ်!", + + "home.invitation.request.msg":"ဖိတ်ကြားမှု တောင်းဆိုသည်ကို လုပ်ဆောင်နေပါသည်!", "sms.verify.title":"သင့်နံပါတ်ကိုအတည်ပြုပါ", "sms.six.digit":"SMS ဂဏန်း ခြောက်လုံး ကိုရိုက်ထဲ့ပါ", @@ -60,11 +62,43 @@ "invite.request":"ဖိတ်ကြားမှု တောင်းဆိုမည်", "invite.request.successful":"တောင်းဆိုမှု အောင်မြင်သည်!", - "================================================================":"", + "customer.list.title":"ဝယ်ယူသူများ", + "customer.name":"နာမည်", + "customer.phone":"ဖုန်းနံပါတ်", + "customer.status":"အခြေအနေ", + "customer.fcs.id":"FCS ID", + "customer.invitation.request.confirm":"လက်ခံ လိုက်ပါ", + "invitation.list": "ဖိတ်ကြားမှုများ", + "invitation.edit": "ဖိတ်ကြားမှု", + "invitation.new": "ဖိတ်ကြားမှု အသစ်", + "invitation.confirm.delete": "ဖိတ်ကြားမှု ဖျက်မလား?", + + "staff.title":"ဝန်ထမ်းများ", + "staff.list.title":"ဝန်ထမ်းများ", + "staff.new":"ဝန်ထမ်း အသစ်", + "staff.form.title":"ဝန်ထမ်း", + "staff.add":"အသစ်ထည့်မည်", + "staff.update":"ပြုပြင်မည်", + + "profile.title":"ကျွန်ုပ် ပရိုဖိုင်", + "profile.edit_title":"ကျွန်ုပ် ပရိုဖိုင်ကိုပြုပြင်ရန်", + "profile.name":"နာမည်", + "profile.phone": "ဖုန်းနံပါတ်", + "profile.language": "ဘာသာစကားများ", + "profile.logout": "အကောင့်ထွက်ရန်", + "profile.usa.shipping.address": "အမေရိကား ပစည်းပို့ရန်လိပ်စာ", + "profile.logout.confirm":"အကောင့်ထွက်ရန်သေချာပြီလား?", + "profile.devices":"ဖုန်းမော်ဒယ်အမျိုးအစားများ", + "profile.email":"အီးမေးလ်", + "profile.privilege":"လုပ်ပိုင်ခွင့်", "btn.save":"သိမ်းဆည်းရန်", "btn.approve":"အတည်ပြုရန်", + "btn.delete":"ဖျက်ရန်", + "================================================================":"", + + "product": "ကုန်ပစ္စည်း", "price": "ဈေးနှုန်း", @@ -166,17 +200,6 @@ "reg.confirm":"မှတ်ပုံတင်သွင်းမည်လား?", "reg.date":"မှတ်ပုံတင်သည့် နေ့စွဲ", - "profile.title":"ပရိုဖိုင်", - "profile.edit_title":"ပရိုဖိုင်ကိုပြုပြင်ရန်", - "profile.name":"နာမည်", - "profile.phone": "ဖုန်းနံပါတ်", - "profile.language": "ဘာသာစကားများ", - "profile.logout": "အကောင့်ထွက်ရန်", - "profile.logout.confirm":"အကောင့်ထွက်ရန်သေချာပြီလား?", - "profile.devices":"ဖုန်းမော်ဒယ်အမျိုးအစားများ", - "profile.email":"အီးမေးလ်", - "profile.privilege":"လုပ်ပိုင်ခွင့်", - "device.confirm":"ဒီဖုန်းမော်ဒယ်ကိုအတည်ပြုမည်လား?", "device.logout":"ဒီဖုန်းမော်ဒယ်ကိုထွက်ရန်သေချာပြီလား?", "device.set_primary":"ဒီဖုန်းမော်ဒယ်ကိုမူလမော်ဒယ်ထည့်ရန်သေချာပြီလား?", @@ -561,19 +584,11 @@ "notifications.title":"Notifications", - "staff.title":"FCS ဝန်ထမ်းများ", - "staff.list.title":"FCS ဝန်ထမ်းများ", - "staff.new":"New Staff", - "staff.form.title":"FCS STAFF", - "staff.add":"Add", - "staff.update":"Update", - "shipment.title":"FCS တင်ပို့ခြင်းများ", "shipment.list.title":"FCS တင်ပို့ခြင်းများ", "shipment.add":"New FCS shipment", "shipment.form.title":"FCS SHIPMENT", "shipment.number":"FCS Shipment Number", - "package.name":"Packages", "package.title":"PACKAGES", @@ -611,7 +626,6 @@ "customers.btn": "ဝယ်ယူသူများ", "customers.title": "ဝယ်ယူသူများ", - "customer.invite":"Invite", "invoices.btn": "ငွေတောင်းခံလွှာများ", "invoices.title": "ငွေတောင်းခံလွှာများ", @@ -622,8 +636,6 @@ "invoice.add_box":"Add Box", - "customer.list.title":"ဝယ်ယူသူများ", - "customer.form.title":"ဝယ်ယူသူ", "fcs.profile": "ပရိုဖိုင်", diff --git a/lib/app.dart b/lib/app.dart index 96f9809..d6b028c 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,6 +1,5 @@ import 'dart:io'; -import 'package:fcs/fcs/common/helpers/theme.dart'; import 'package:fcs/fcs/common/localization/app_translations_delegate.dart'; import 'package:fcs/fcs/common/localization/transalation.dart'; import 'package:fcs/fcs/common/pages/contact/model/contact_model.dart'; @@ -8,7 +7,8 @@ import 'package:fcs/fcs/common/pages/customers/model/customer_model.dart'; import 'package:fcs/fcs/common/pages/faq/model/faq_model.dart'; import 'package:fcs/fcs/common/pages/initial_language_selection.dart'; import 'package:fcs/fcs/common/pages/model/language_model.dart'; -import 'package:fcs/fcs/common/pages/signin/model/signin_model.dart'; +import 'package:fcs/fcs/common/pages/model/main_model.dart' as fcs; +import 'package:fcs/fcs/common/pages/staff/model/staff_model.dart'; import 'package:fcs/fcs/common/pages/term/model/term_model.dart'; import 'package:fcs/fcs/common/services/services.dart'; import 'package:fcs/model/buyer_model.dart'; @@ -31,8 +31,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; -import 'package:fcs/fcs/common/pages/model/main_model.dart' as fcs; +import 'fcs/common/pages/home_page.dart'; +import 'fcs/common/pages/splash_page.dart'; +import 'fcs/common/pages/welcome_page.dart'; import 'model/announcement_model.dart'; import 'model/chart_model.dart'; import 'model/device_model.dart'; @@ -48,9 +50,6 @@ import 'model/report_user_model.dart'; import 'model/shipment_rate_model.dart'; import 'model/user_model.dart'; import 'model_fcs/box_model.dart'; -import 'fcs/common/pages/home_page.dart'; -import 'fcs/common/pages/welcome_page.dart'; -import 'fcs/common/pages/splash_page.dart'; import 'pages/term.dart'; class App extends StatefulWidget { @@ -67,7 +66,6 @@ class _AppState extends State { final UserModel userModel = new UserModel(); final ProductModel productModel = new ProductModel(); - final EmployeeModel employeeModel = new EmployeeModel(); final POSubmissionModel poSubmissionModel = new POSubmissionModel(); final DOModel doModel = new DOModel(); final LanguageModel lanuguageModel = new LanguageModel(); @@ -94,7 +92,7 @@ class _AppState extends State { final InvoiceModel invoiceModel = new InvoiceModel(); final CustomerModel customerModel = new CustomerModel(); final DiscountModel discountModel = new DiscountModel(); - final SigninModel signinModel = new SigninModel(); + final StaffModel staffModel = new StaffModel(); AppTranslationsDelegate _newLocaleDelegate; static FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = @@ -103,12 +101,14 @@ class _AppState extends State { @override void initState() { super.initState(); + mainModel2..addModel(customerModel); + mainModel2..addModel(staffModel); mainModel2.init(); + _newLocaleDelegate = AppTranslationsDelegate(newLocale: null); Translation().onLocaleChanged = onLocaleChange; mainModel ..addModel(userModel) - ..addModel(employeeModel) ..addModel(storageModel) ..addModel(regModel) ..addModel(poSubmissionModel) @@ -225,7 +225,7 @@ class _AppState extends State { ChangeNotifierProvider.value(value: mainModel), ChangeNotifierProvider.value(value: userModel), ChangeNotifierProvider.value(value: productModel), - ChangeNotifierProvider.value(value: employeeModel), + ChangeNotifierProvider.value(value: staffModel), ChangeNotifierProvider.value(value: poSubmissionModel), ChangeNotifierProvider.value(value: doModel), ChangeNotifierProvider.value(value: storageModel), @@ -255,7 +255,6 @@ class _AppState extends State { ChangeNotifierProvider.value(value: contactModel), ChangeNotifierProvider.value(value: termModel), ChangeNotifierProvider.value(value: faqModel), - ChangeNotifierProvider.value(value: signinModel), ], child: Consumer( builder: (context, value, child) { diff --git a/lib/fcs/common/data/providers/auth_fb.dart b/lib/fcs/common/data/providers/auth_fb.dart index 8f1eb87..2f357f3 100644 --- a/lib/fcs/common/data/providers/auth_fb.dart +++ b/lib/fcs/common/data/providers/auth_fb.dart @@ -9,10 +9,13 @@ import 'package:fcs/fcs/common/domain/entities/setting.dart'; import 'package:fcs/fcs/common/domain/entities/user.dart'; import 'package:fcs/fcs/common/domain/exceiptions/signin_exception.dart'; import 'package:firebase_auth/firebase_auth.dart'; +import 'package:logging/logging.dart'; import '../../helpers/api_helper.dart'; class AuthFb { + final log = Logger('AuthFb'); + static final AuthFb instance = AuthFb._(); AuthFb._(); @@ -21,12 +24,22 @@ class AuthFb { Future sendSmsCodeToPhoneNumber(String phoneNumber) { Completer completer = Completer(); + bool codeSentCompleted = false; final PhoneVerificationCompleted verificationCompleted = (AuthCredential credential) async { - AuthResult _authResult = await _fb.signInWithCredential(credential); - if (_authResult == null) { - throw SigninException("Sigin error!"); + AuthResult _authResult; + try { + _authResult = await _fb.signInWithCredential(credential); + print("PhoneVerificationCompleted :$_authResult"); + if (_authResult == null) { + throw SigninException("Sigin error!"); + } + } catch (e) { + print("Exception:$e"); + // throw e; + completer.completeError(SigninException(e.toString())); + return; } fcs.AuthResult auth = fcs.AuthResult(authStatus: AuthStatus.AUTH_VERIFIED); @@ -39,25 +52,32 @@ class AuthFb { (AuthException authException) async { print( 'Phone number verification failed. Code: ${authException.code}. Message: ${authException.message}'); - completer - .completeError(SigninException("Phone number verification failed")); + completer.completeError(SigninException( + "Phone number verification failed:${authException.message}")); }; final PhoneCodeSent codeSent = (String verificationId, [int forceResendingToken]) async { _verificationId = verificationId; - print("code sent to " + phoneNumber); - completer.complete(fcs.AuthResult(authStatus: AuthStatus.SMS_SENT)); + print("codeSent " + phoneNumber); + codeSentCompleted = true; }; final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout = (String verificationId) { + print("codeAutoRetrievalTimeout $verificationId "); + _verificationId = verificationId; + if (codeSentCompleted) { + completer.complete(fcs.AuthResult(authStatus: AuthStatus.SMS_SENT)); + } else { + completer.completeError(SigninException("SMS code failed")); + } }; _fb.verifyPhoneNumber( phoneNumber: phoneNumber, - timeout: const Duration(minutes: 2), + timeout: const Duration(seconds: 0), verificationCompleted: verificationCompleted, verificationFailed: verificationFailed, codeSent: codeSent, @@ -102,12 +122,12 @@ class AuthFb { if (firebaseUser == null) return null; IdTokenResult idToken = await firebaseUser.getIdToken(refresh: refreshIdToken); - String name = idToken.claims["name"]; + + log.info("Claims:${idToken.claims}"); + User user = User(); user.id = firebaseUser.uid; - user.name = name; - user.hasSignup = - idToken.claims.containsKey("signup") && idToken.claims["signup"]; + user.status = idToken.claims["status"]; user.phoneNumber = firebaseUser.phoneNumber; // add privileges @@ -115,10 +135,24 @@ class AuthFb { if (privileges != null && privileges != "") { user.privileges = privileges.split(":").toList(); } - + User _user = await getUserFromFirestore(user.id); + user.fcsID = _user.fcsID; + user.name = _user.name; return user; } + Future getUserFromFirestore(String userID) async { + DocumentSnapshot snap = await Firestore.instance + .collection(user_collection) + .document(userID) + .get(); + if (snap.exists) { + User user = User.fromMap(snap.data, snap.documentID); + return user; + } + return null; + } + Future isLogin() async { final FirebaseUser firebaseUser = await _fb.currentUser(); return Future.value(firebaseUser != null); @@ -134,6 +168,17 @@ class AuthFb { return getUser(refreshIdToken: true); } + Future hasInvite() async { + var invited = + await requestAPI("/check_invitation", "GET", token: await getToken()); + return invited["invited"]; + } + + Future updateProfile(String newUserName) async { + return await requestAPI("/profile", "PUT", + payload: {"user_name": newUserName}, token: await getToken()); + } + Future getToken() async { FirebaseUser firebaseUser = await _fb.currentUser(); IdTokenResult token = await firebaseUser.getIdToken(); diff --git a/lib/fcs/common/data/providers/user_data_provider.dart b/lib/fcs/common/data/providers/user_data_provider.dart new file mode 100644 index 0000000..0dbefc7 --- /dev/null +++ b/lib/fcs/common/data/providers/user_data_provider.dart @@ -0,0 +1,38 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:fcs/fcs/common/domain/constants.dart'; +import 'package:fcs/fcs/common/domain/entities/user.dart'; +import 'package:fcs/fcs/common/helpers/api_helper.dart'; +import 'package:fcs/fcs/common/helpers/firebase_helper.dart'; + +class UserDataProvider { + Future inviteUser(String userName, String phoneNumber) async { + return await requestAPI("/invites", "POST", + payload: {"user_name": userName, "phone_number": phoneNumber}, + token: await getToken()); + } + + Future deleteInvite(String phoneNumber) async { + return await requestAPI("/invites", "DELETE", + payload: {"phone_number": phoneNumber}, token: await getToken()); + } + + Future acceptRequest(String userID) async { + return await requestAPI("/invites", "PUT", + payload: {"id": userID}, token: await getToken()); + } + + Future findUser(String phoneNumber) async { + QuerySnapshot querySnap = await Firestore.instance + .collection(user_collection) + .where("phone_number", isEqualTo: phoneNumber) + .limit(1) + .getDocuments(); + + if (querySnap.documents.length > 0) { + var snap = querySnap.documents.first; + User user = User.fromMap(snap.data, snap.documentID); + return user; + } + return null; + } +} diff --git a/lib/fcs/common/data/providers/user_fb_data_provider.dart b/lib/fcs/common/data/providers/user_fb_data_provider.dart deleted file mode 100644 index d5e836c..0000000 --- a/lib/fcs/common/data/providers/user_fb_data_provider.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:fcs/fcs/common/domain/entities/user.dart'; - -class UserFBDataProvider { - Future getUser(String id) { - return null; - } -} diff --git a/lib/fcs/common/domain/constants.dart b/lib/fcs/common/domain/constants.dart index c7d22eb..0dd861b 100644 --- a/lib/fcs/common/domain/constants.dart +++ b/lib/fcs/common/domain/constants.dart @@ -1,11 +1,13 @@ -const ok_doc_id = "ok"; -const setting_doc_id = "setting"; - const config_collection = "configs"; +const user_collection = "users"; +const invitations_collection = "invitations"; +const setting_doc_id = "setting"; +const privilege_collection = "privileges"; + +const ok_doc_id = "ok"; + const biz_collection = "bizs"; const product_collection = "products"; -const user_collection = "users"; -const privilege_collection = "privileges"; const user_level_collection = "user_levels"; const storage_collection = "storages"; const buyer_collection = "buyers"; diff --git a/lib/fcs/common/domain/entities/role.dart b/lib/fcs/common/domain/entities/role.dart index 6a2b0df..d0e5aae 100644 --- a/lib/fcs/common/domain/entities/role.dart +++ b/lib/fcs/common/domain/entities/role.dart @@ -9,7 +9,6 @@ class Role { roleID = json['role_id']; privileges = json['privileges']; } - } class Parser { @@ -63,10 +62,6 @@ class UserLevel { UserLevel({this.id, this.name, this.level}); factory UserLevel.fromMap(Map map, String docID) { - return UserLevel( - id: docID, - name: map['name'], - level: map['level'] - ); + return UserLevel(id: docID, name: map['name'], level: map['level']); } } diff --git a/lib/fcs/common/domain/entities/setting.dart b/lib/fcs/common/domain/entities/setting.dart index 22a0e02..0e0b1c4 100644 --- a/lib/fcs/common/domain/entities/setting.dart +++ b/lib/fcs/common/domain/entities/setting.dart @@ -22,6 +22,8 @@ class Setting { String mmContactNumber; String emailAddress; String facebookLink; + bool inviteRequired; + String appUrl; final String okEnergyId; final String about; @@ -84,6 +86,8 @@ class Setting { this.mmContactNumber, this.emailAddress, this.facebookLink, + this.inviteRequired, + this.appUrl, this.about, this.okEnergyId, this.terms, @@ -122,6 +126,8 @@ class Setting { return Setting( supportBuildNum: map['support_build_number'], + inviteRequired: map['invite_required'], + appUrl: map['app_url'], usaAddress: map['usa_address'], mmAddress: map['mm_address'], usaContactNumber: map['usa_contact_number'], diff --git a/lib/fcs/common/domain/entities/user.dart b/lib/fcs/common/domain/entities/user.dart index 9f4ff40..8f8f7b9 100644 --- a/lib/fcs/common/domain/entities/user.dart +++ b/lib/fcs/common/domain/entities/user.dart @@ -1,22 +1,28 @@ +import 'package:fcs/fcs/common/helpers/const.dart'; + class User { String id; String name; String phoneNumber; - bool hasSignup; - bool invited; - List privileges = []; - + String status; String fcsID; + List privileges = []; + String get phone => phoneNumber != null && phoneNumber.startsWith("959") ? "0${phoneNumber.substring(2)}" : phoneNumber; - + bool get joined => status != null && status == userStatusJoined; + bool get invited => status != null && status == userStatusInvited; + bool get requested => status != null && status == userStatusRequested; + String get share => "Your phone number:$phoneNumber"; User({ this.id, this.name, this.phoneNumber, this.fcsID, + this.status, + this.privileges, }); factory User.fromJson(Map json) { @@ -24,6 +30,7 @@ class User { id: json['id'], name: json['user_name'], phoneNumber: json['phone_number'], + status: json['status'], ); } @@ -41,11 +48,16 @@ class User { } factory User.fromMap(Map map, String docID) { + List _privileges = + map['privileges'] == null ? [] : map['privileges'].cast(); + return User( - id: docID, - name: map['user_name'], - phoneNumber: map['phone_number'], - ); + id: docID, + name: map['user_name'], + phoneNumber: map['phone_number'], + status: map['status'], + fcsID: map['fcs_id'], + privileges: _privileges); } bool isCustomer() { @@ -60,16 +72,26 @@ class User { return privileges != null ? privileges.contains('admin') : false; } - bool hasMaintenance() { - return privileges != null ? privileges.contains('mt') : false; + bool hasCustomers() { + return hasSysAdmin() || + hasAdmin() || + (privileges != null ? privileges.contains('c') : false); } - bool hasCustomers() { - return privileges != null ? privileges.contains('c') : false; + bool hasStaffs() { + return hasSysAdmin() || + hasAdmin() || + (privileges != null ? privileges.contains('s') : false); + } + + bool hasSupport() { + return hasSysAdmin() || + hasAdmin() || + (privileges != null ? privileges.contains('sp') : false); } @override String toString() { - return 'User{name: $name, phoneNumber: $phoneNumber,hasSignup:$hasSignup}'; + return 'User{name: $name, phoneNumber: $phoneNumber,status:$status}'; } } diff --git a/lib/fcs/common/helpers/const.dart b/lib/fcs/common/helpers/const.dart new file mode 100644 index 0000000..f558a5e --- /dev/null +++ b/lib/fcs/common/helpers/const.dart @@ -0,0 +1,3 @@ +const userStatusInvited = "invited"; +const userStatusJoined = "joined"; +const userStatusRequested = "requested"; diff --git a/lib/fcs/common/pages/customers/customer_editor.dart b/lib/fcs/common/pages/customers/customer_editor.dart index a740afd..899b07c 100644 --- a/lib/fcs/common/pages/customers/customer_editor.dart +++ b/lib/fcs/common/pages/customers/customer_editor.dart @@ -1,363 +1,128 @@ -import 'package:fcs/fcs/common/domain/entities/customer.dart'; -import 'package:fcs/fcs/common/domain/entities/role.dart'; import 'package:fcs/fcs/common/domain/entities/user.dart'; -import 'package:fcs/fcs/common/localization/app_translations.dart'; -import 'package:fcs/fcs/common/pages/model/language_model.dart'; +import 'package:fcs/fcs/common/helpers/theme.dart'; +import 'package:fcs/fcs/common/pages/customers/model/customer_model.dart'; import 'package:fcs/fcs/common/pages/util.dart'; +import 'package:fcs/fcs/common/pages/widgets/display_text.dart'; import 'package:fcs/fcs/common/pages/widgets/local_text.dart'; import 'package:fcs/fcs/common/pages/widgets/progress.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:fcs/fcs/common/helpers/theme.dart'; typedef void FindCallBack(); class CustomerEditor extends StatefulWidget { - final Customer customer; + final User customer; const CustomerEditor({this.customer}); @override _CustomerEditorState createState() => _CustomerEditorState(); } class _CustomerEditorState extends State { - TextEditingController _name = new TextEditingController(); - TextEditingController _phone = new TextEditingController(); - TextEditingController _phoneInput = new TextEditingController(); - TextEditingController _status = new TextEditingController(); - - final _formKey = GlobalKey(); bool _isLoading = false; - String currentBizId; - bool isSend = false; - User user; - User selectedUser; - List privileges = [ - Privilege(name: 'Manage shipment'), - Privilege(name: 'Manage pickups'), - Privilege(name: 'Manage packages'), - Privilege(name: 'Manage deliveries'), - Privilege(name: 'Admin') - ]; @override void initState() { super.initState(); - // privileges = Provider.of(context, listen: false).privileges; - if (widget.customer != null) { - _name.text = widget.customer.name; - _phone.text = widget.customer.phoneNumber; - _status.text = widget.customer.status; - // privileges.forEach((p) => widget.employee.privilegeIds.contains(p.id) - // ? p.isChecked = true - // : p.isChecked = false); - } - } - - List showprivilegeList(BuildContext context) { - return privileges.map((p) { - return new ListTile( - title: new Row( - children: [ - new Checkbox( - value: p.isChecked == null ? false : p.isChecked, - activeColor: primaryColor, - onChanged: (bool value) { - setState(() { - p.isChecked = value; - }); - }), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - new Text( - p.name, - style: TextStyle( - fontSize: 15.0, - ), - ), - // Container( - // width: MediaQuery.of(context).size.width * 0.5, - // child: new Text( - // userModel.getPrivileges[index].desc, - // style: - // TextStyle(fontSize: 12.0, color: Colors.grey[600]), - // ), - // ), - ], - ), - ], - )); - }).toList(); - } - - Widget phoneInputbox(BuildContext context, FindCallBack findCallBack) { - var languageModel = Provider.of(context); - return Container( - padding: EdgeInsets.only(top: 10), - child: Stack( - alignment: const Alignment(1.2, 1.0), - children: [ - TextFormField( - controller: _phoneInput, - autofocus: false, - cursorColor: primaryColor, - keyboardType: TextInputType.phone, - style: textStyle, - decoration: new InputDecoration( - labelText: AppTranslations.of(context).text('employee.phone'), - labelStyle: languageModel.isEng ? labelStyle : labelStyleMM, - icon: Icon( - Icons.phone, - color: primaryColor, - ), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: primaryColor, width: 1.0)), - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: primaryColor, width: 1.0)), - ), - ), - new FlatButton( - onPressed: () { - this.isSend = true; - findCallBack(); - }, - child: new Icon( - Icons.search, - size: 25, - )) - ], - )); } @override Widget build(BuildContext context) { - final namebox = TextFormField( - controller: _name, - autofocus: false, - readOnly: true, - cursorColor: primaryColor, - decoration: new InputDecoration( - border: InputBorder.none, - focusedBorder: InputBorder.none, - icon: Icon( - Icons.person, - color: primaryColor, - ), - ), - ); - - final displayPhoneNo = TextFormField( - controller: _phone, - autofocus: false, - readOnly: true, - cursorColor: primaryColor, - decoration: new InputDecoration( - border: InputBorder.none, - focusedBorder: InputBorder.none, - icon: Icon( - Icons.phone, - color: primaryColor, - ), - ), - ); - var phoneNumberBox = Row( children: [ - Expanded(child: displayPhoneNo), Expanded( - child: InkWell( - onTap: () => call(context, _phone.text), - child: Icon( - Icons.open_in_new, - color: Colors.grey, - size: 15, - ), - ), - ), + child: DisplayText( + text: widget.customer.phoneNumber, + labelText: getLocalString(context, "customer.phone"), + iconData: Icons.phone, + )), + IconButton( + icon: Icon(Icons.open_in_new, color: primaryColor), + onPressed: () => call(context, widget.customer.phoneNumber)), ], ); - final statusbox = TextFormField( - controller: _status, - autofocus: false, - readOnly: true, - cursorColor: primaryColor, - decoration: new InputDecoration( - border: InputBorder.none, - focusedBorder: InputBorder.none, - icon: Icon( - Icons.av_timer, - color: primaryColor, - ), - ), - ); - - final updateButton = Container( - padding: EdgeInsets.only(top: 40), - child: Container( - height: 45.0, - decoration: BoxDecoration( - color: primaryColor, - shape: BoxShape.rectangle, - ), - child: ButtonTheme( - minWidth: 900.0, - height: 100.0, - child: FlatButton( - onPressed: () {}, - child: LocalText( - context, - 'customer.update', - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ); - - final addButton = Container( - padding: EdgeInsets.only(top: 40), - child: Container( - height: 45.0, - decoration: BoxDecoration( - color: primaryColor, - shape: BoxShape.rectangle, - ), - child: ButtonTheme( - minWidth: 900.0, - height: 100.0, - child: FlatButton( - onPressed: () {}, - child: LocalText( - context, - 'customer.add', - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ); - return LocalProgress( inAsyncCall: _isLoading, - child: Scaffold( - appBar: AppBar( - centerTitle: true, - leading: new IconButton( - icon: new Icon(Icons.close), - onPressed: () => Navigator.of(context).pop(), + child: SafeArea( + child: Scaffold( + appBar: AppBar( + backgroundColor: Colors.white, + shadowColor: Colors.transparent, + centerTitle: true, + leading: new IconButton( + icon: new Icon( + Icons.close, + color: primaryColor, + size: 30, + ), + onPressed: () => Navigator.of(context).pop(), + ), + title: Text( + widget.customer.name, + style: TextStyle( + fontSize: 20, + color: primaryColor, + ), + ), ), - backgroundColor: primaryColor, - title: LocalText( - context, - "customer.form.title", - fontSize: 20, - color: Colors.white, + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 18.0, right: 18), + child: ListView( + children: [ + phoneNumberBox, + DisplayText( + text: widget.customer.status, + labelText: + getLocalString(context, "customer.status"), + iconData: Icons.add_alarm, + ), + DisplayText( + text: widget.customer.fcsID, + labelText: + getLocalString(context, "customer.fcs.id"), + iconData: Icons.account_circle, + ), + ], + ), + ), + ), + widget.customer.requested + ? fcsButton( + context, + getLocalString( + context, "customer.invitation.request.confirm"), + callack: _add) + : Container() + ], + ), ), - // actions: [ - // widget.customer == null || !mainModel.showHistoryBtn() - // ? Container() - // : IconButton( - // icon: Icon(Icons.history), - // onPressed: () { - // Navigator.push( - // context, - // MaterialPageRoute( - // builder: (context) => DocumentLogPage( - // docID: widget.customer.docID)), - // ); - // }, - // ), - // ], - ), - body: ListView( - shrinkWrap: true, - padding: EdgeInsets.only(left: 24.0, right: 24.0), - children: [ - widget.customer == null - ? phoneInputbox(context, () => _findUser(context)) - : phoneNumberBox, - widget.customer == null - ? this.isSend ? namebox : Container() - : namebox, - statusbox, - // widget.customer == null ? addButton : updateButton, - SizedBox( - height: 20, - ) - ], ), )); } - _add(BuildContext context) async { - if (selectedUser == null) return; - setState(() { - _isLoading = true; + _add() async { + showConfirmDialog(context, "customer.invitation.request.confirm", () async { + setState(() { + _isLoading = true; + }); + if (widget.customer == null) return; + CustomerModel customerModel = + Provider.of(context, listen: false); + try { + await customerModel.acceptRequest(widget.customer.id); + Navigator.pop(context); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } }); - // var employeeModel = Provider.of(context); - // try { - // await employeeModel.updatePrivileges( - // this.selectedUser.docID, privilegesIDs()); - // Navigator.pop(context); - // } catch (e) { - // showMsgDialog(context, "Error", e.toString()); - // } finally { - // setState(() { - // _isLoading = false; - // }); - // } - } - - List privilegesIDs() { - return this.privileges.where((p) => p.isChecked).map((p) => p.id).toList(); - } - - _save() async { - setState(() { - _isLoading = true; - }); - if (widget.customer == null) return; - // var employeeModel = Provider.of(context); - // try { - // await employeeModel.updatePrivileges( - // widget.customer.docID, privilegesIDs()); - // Navigator.pop(context); - // } catch (e) { - // showMsgDialog(context, "Error", e.toString()); - // } finally { - // setState(() { - // _isLoading = false; - // }); - // } - } - - _findUser(BuildContext context) async { - // var userModel = Provider.of(context); - // setState(() { - // _isLoading = true; - // }); - // try { - // selectedUser = await userModel.findUser(_phoneInput.text); - // setState(() { - // isSend = true; - // _name.text = selectedUser.name; - // if (selectedUser.privilegeIds != null) { - // privileges.forEach((p) => selectedUser.privilegeIds.contains(p.id) - // ? p.isChecked = true - // : p.isChecked = false); - // } - // }); - // } catch (e) { - // showMsgDialog(context, "Error", e.toString()); - // } finally { - // setState(() { - // _isLoading = false; - // }); - // } } } diff --git a/lib/fcs/common/pages/customers/customer_list.dart b/lib/fcs/common/pages/customers/customer_list.dart index aae62c8..c0978f5 100644 --- a/lib/fcs/common/pages/customers/customer_list.dart +++ b/lib/fcs/common/pages/customers/customer_list.dart @@ -1,20 +1,18 @@ -import 'package:fcs/fcs/common/domain/entities/customer.dart'; import 'package:fcs/fcs/common/domain/entities/user.dart'; -import 'package:fcs/fcs/common/localization/app_translations.dart'; -import 'package:fcs/fcs/common/pages/customers/invitation_page.dart'; +import 'package:fcs/fcs/common/helpers/theme.dart'; +import 'package:fcs/fcs/common/pages/customers/customer_editor.dart'; +import 'package:fcs/fcs/common/pages/customers/invitation_list.dart'; import 'package:fcs/fcs/common/pages/customers/model/customer_model.dart'; import 'package:fcs/fcs/common/pages/util.dart'; import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart'; import 'package:fcs/fcs/common/pages/widgets/local_text.dart'; +import 'package:fcs/widget/progress.dart'; import 'package:flutter/material.dart'; import 'package:flutter_icons/flutter_icons.dart'; import 'package:intl/intl.dart'; - import 'package:provider/provider.dart'; -import 'package:fcs/fcs/common/helpers/theme.dart'; -import 'package:fcs/widget/progress.dart'; -import 'customer_editor.dart'; +import 'invitation_editor.dart'; class CustomerList extends StatefulWidget { @override @@ -40,14 +38,14 @@ class _CustomerListState extends State { onPressed: () => Navigator.of(context).pop(), ), actions: [ - IconButton( - icon: Icon( - Icons.search, - color: Colors.white, - ), - iconSize: 30, - onPressed: () => {}, - ), + // IconButton( + // icon: Icon( + // Icons.search, + // color: Colors.white, + // ), + // iconSize: 30, + // onPressed: () => {}, + // ), ], backgroundColor: primaryColor, title: LocalText( @@ -57,86 +55,114 @@ class _CustomerListState extends State { fontSize: 20, ), ), - floatingActionButton: FloatingActionButton.extended( - onPressed: () { - Navigator.of(context).push(BottomUpPageRoute(InvitationPage())); - }, - icon: Icon(Icons.add), - label: Text(AppTranslations.of(context).text("customer.invite")), - backgroundColor: primaryColor, - ), - body: new ListView.separated( - separatorBuilder: (context, index) => Divider( - color: Colors.black, - ), - scrollDirection: Axis.vertical, - padding: EdgeInsets.only(left: 15, right: 15, top: 15), - shrinkWrap: true, - itemCount: customerModel.customers.length, - itemBuilder: (BuildContext context, int index) { - Customer customer = customerModel.customers[index]; - return Stack( - children: [ - InkWell( - onTap: () { - Navigator.of(context).push( - BottomUpPageRoute(CustomerEditor(customer: customer))); - }, - child: Row( - children: [ - Expanded( - child: new Padding( - padding: const EdgeInsets.symmetric(vertical: 10.0), - child: new Row( - children: [ - new Padding( - padding: new EdgeInsets.symmetric( - horizontal: 32.0 - dotSize / 2), - child: Icon( - Feather.user, - color: primaryColor, - size: 40, - ), - ), - new Expanded( - child: new Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - new Text( - customer.name, - style: new TextStyle( - fontSize: 15.0, - color: primaryColor), - ), - Padding( - padding: - const EdgeInsets.only(top: 8.0), - child: new Text( - customer.phoneNumber, - style: new TextStyle( - fontSize: 15.0, - color: Colors.grey), - ), - ), - ], - ), - ), - ], - ), - ), - ), - Padding( - padding: const EdgeInsets.only(right: 10), - child: getStatus(customer.status), - ), - ], + body: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.only(top: 3, right: 5), + child: InkWell( + onTap: _invitations, + child: Container( + color: primaryColor, + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Text( + "Invitations", + style: TextStyle(color: Colors.white), ), ), - ], - ); - }), + ), + ), + ), + Expanded( + child: ListView.separated( + separatorBuilder: (context, index) => Divider( + color: Colors.black, + ), + scrollDirection: Axis.vertical, + padding: EdgeInsets.only(left: 15, right: 15), + shrinkWrap: true, + itemCount: customerModel.customers.length, + itemBuilder: (BuildContext context, int index) { + User customer = customerModel.customers[index]; + return _item(customer); + }), + ), + ], + ), ), ); } + + Widget _item(User customer) { + return Stack( + children: [ + InkWell( + onTap: () => _select(customer), + child: Row( + children: [ + Expanded( + child: new Padding( + padding: const EdgeInsets.symmetric(vertical: 10.0), + child: new Row( + children: [ + new Padding( + padding: new EdgeInsets.symmetric( + horizontal: 32.0 - dotSize / 2), + child: Icon( + Feather.user, + color: primaryColor, + size: 40, + ), + ), + new Expanded( + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Text( + customer.name, + style: new TextStyle( + fontSize: 15.0, color: primaryColor), + ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: new Text( + customer.phoneNumber, + style: new TextStyle( + fontSize: 15.0, color: Colors.grey), + ), + ), + ], + ), + ), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.only(right: 10), + child: _status(customer.status), + ), + ], + ), + ), + ], + ); + } + + Widget _status(String status) { + return Text( + status == "requested" ? status : "", + style: TextStyle(color: primaryColor, fontSize: 14), + ); + } + + _select(User customer) { + Navigator.of(context) + .push(BottomUpPageRoute(CustomerEditor(customer: customer))); + } + + _invitations() { + Navigator.of(context).push(BottomUpPageRoute(InvitationList())); + } } diff --git a/lib/fcs/common/pages/customers/invitation_detail.dart b/lib/fcs/common/pages/customers/invitation_detail.dart new file mode 100644 index 0000000..3420bb2 --- /dev/null +++ b/lib/fcs/common/pages/customers/invitation_detail.dart @@ -0,0 +1,159 @@ +import 'package:country_code_picker/country_code_picker.dart'; +import 'package:fcs/fcs/common/helpers/theme.dart'; +import 'package:fcs/fcs/common/localization/app_translations.dart'; +import 'package:fcs/fcs/common/pages/customers/model/customer_model.dart'; +import 'package:fcs/fcs/common/pages/util.dart'; +import 'package:fcs/widget/progress.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class InvitationDetail extends StatefulWidget { + @override + _InvitationDetailState createState() => _InvitationDetailState(); +} + +class _InvitationDetailState extends State { + TextEditingController _nameController = new TextEditingController(); + TextEditingController _phoneController = new TextEditingController(); + + bool _isLoading = false; + String dialCode; + + @override + void initState() { + super.initState(); + dialCode = "+95"; + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: new Icon( + Icons.close, + ), + onPressed: () => Navigator.of(context).pop(), + ), + backgroundColor: primaryColor, + title: Text(AppTranslations.of(context).text("invitation.new")), + ), + body: Container( + padding: EdgeInsets.all(18), + child: Column( + children: [ + Expanded( + child: ListView( + children: [ + fcsInput("Name", Icons.person, + controller: _nameController, autoFocus: true), + SizedBox(height: 10), + Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + Icons.phone, + color: primaryColor, + ), + ), + Container( + decoration: BoxDecoration( + border: + Border.all(color: Colors.grey[400], width: 1), + borderRadius: + BorderRadius.all(Radius.circular(12.0))), + child: CountryCodePicker( + onChanged: _countryChange, + initialSelection: dialCode, + countryFilter: ['+95', '+1'], + showCountryOnly: false, + showOnlyCountryWhenClosed: false, + alignLeft: false, + textStyle: TextStyle( + fontSize: 16, + ), + ), + ), + SizedBox( + width: 10, + ), + Flexible( + child: Container( + padding: EdgeInsets.only(top: 10, bottom: 10), + child: TextFormField( + controller: _phoneController, + cursorColor: primaryColor, + textAlign: TextAlign.left, + keyboardType: TextInputType.phone, + style: TextStyle( + fontSize: 18, + ), + decoration: InputDecoration( + fillColor: Colors.white, + labelText: "Phone number", + labelStyle: + TextStyle(fontSize: 16, color: Colors.grey), + filled: true, + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Colors.grey, width: 1.0)), + ), + ), + ), + ), + ], + ), + ], + ), + ), + fcsButton(context, "Invite", callack: _invite), + SizedBox(height: 10) + ], + ), + ), + ), + ); + } + + _countryChange(CountryCode countryCode) { + setState(() { + dialCode = countryCode.dialCode; + }); + } + + _invite() async { + String userName = _nameController.text; + String phoneNumber = dialCode + _phoneController.text; + if (userName == null || + userName == "" || + phoneNumber == null || + phoneNumber == "") { + showMsgDialog(context, "Error", "Invalid name or phone number"); + return; + } + setState(() { + _isLoading = true; + }); + try { + CustomerModel customerModel = + Provider.of(context, listen: false); + await customerModel.inviteUser(userName, phoneNumber); + Navigator.pop(context); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } +} diff --git a/lib/fcs/common/pages/customers/invitation_editor.dart b/lib/fcs/common/pages/customers/invitation_editor.dart new file mode 100644 index 0000000..02f62b3 --- /dev/null +++ b/lib/fcs/common/pages/customers/invitation_editor.dart @@ -0,0 +1,105 @@ +import 'package:fcs/fcs/common/domain/entities/customer.dart'; +import 'package:fcs/fcs/common/domain/entities/role.dart'; +import 'package:fcs/fcs/common/domain/entities/user.dart'; +import 'package:fcs/fcs/common/localization/app_translations.dart'; +import 'package:fcs/fcs/common/pages/customers/model/customer_model.dart'; +import 'package:fcs/fcs/common/pages/model/language_model.dart'; +import 'package:fcs/fcs/common/pages/util.dart'; +import 'package:fcs/fcs/common/pages/widgets/display_text.dart'; +import 'package:fcs/fcs/common/pages/widgets/local_text.dart'; +import 'package:fcs/fcs/common/pages/widgets/progress.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:fcs/fcs/common/helpers/theme.dart'; + +typedef void FindCallBack(); + +class InvitationEditor extends StatefulWidget { + final User customer; + const InvitationEditor({this.customer}); + @override + _InvitationEditorState createState() => _InvitationEditorState(); +} + +class _InvitationEditorState extends State { + bool _isLoading = false; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + var phoneNumberBox = Row( + children: [ + Expanded( + child: DisplayText( + text: widget.customer.phoneNumber, + labelText: getLocalString(context, "customer.phone"), + iconData: Icons.phone, + )), + IconButton( + icon: Icon(Icons.open_in_new, color: primaryColor), + onPressed: () => call(context, widget.customer.phoneNumber)), + ], + ); + + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + backgroundColor: Colors.white, + shadowColor: Colors.transparent, + centerTitle: true, + leading: new IconButton( + icon: new Icon( + Icons.close, + color: primaryColor, + size: 30, + ), + onPressed: () => Navigator.of(context).pop(), + ), + title: Text( + widget.customer.name, + style: TextStyle(fontSize: 20, color: primaryColor), + ), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Expanded( + child: ListView( + children: [phoneNumberBox], + ), + ), + fcsButton(context, getLocalString(context, "btn.delete"), + callack: _delete) + ], + ), + ), + )); + } + + _delete() async { + showConfirmDialog(context, "invitation.confirm.delete", () async { + setState(() { + _isLoading = true; + }); + if (widget.customer == null) return; + CustomerModel customerModel = + Provider.of(context, listen: false); + try { + await customerModel.deleteInvite(widget.customer.phoneNumber); + Navigator.pop(context); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + }); + } +} diff --git a/lib/fcs/common/pages/customers/invitation_list.dart b/lib/fcs/common/pages/customers/invitation_list.dart new file mode 100644 index 0000000..84e71d6 --- /dev/null +++ b/lib/fcs/common/pages/customers/invitation_list.dart @@ -0,0 +1,148 @@ +import 'package:fcs/fcs/common/pages/model/main_model.dart'; +import 'package:share/share.dart'; +import 'package:fcs/fcs/common/domain/entities/user.dart'; +import 'package:fcs/fcs/common/localization/app_translations.dart'; +import 'package:fcs/fcs/common/pages/customers/invitation_detail.dart'; +import 'package:fcs/fcs/common/pages/customers/model/customer_model.dart'; +import 'package:fcs/fcs/common/pages/util.dart'; +import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart'; +import 'package:fcs/fcs/common/pages/widgets/local_text.dart'; +import 'package:fcs/fcs/common/pages/widgets/progress.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_icons/flutter_icons.dart'; +import 'package:intl/intl.dart'; + +import 'package:provider/provider.dart'; +import 'package:fcs/fcs/common/helpers/theme.dart'; + +import 'invitation_editor.dart'; + +class InvitationList extends StatefulWidget { + @override + _InvitationListState createState() => _InvitationListState(); +} + +class _InvitationListState extends State { + var dateFormatter = new DateFormat('dd MMM yyyy - hh:mm:ss a'); + final double dotSize = 15.0; + bool _isLoading = false; + + @override + Widget build(BuildContext context) { + var customerModel = Provider.of(context); + + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: new Icon(Icons.close), + onPressed: () => Navigator.of(context).pop(), + ), + actions: [], + backgroundColor: primaryColor, + title: LocalText( + context, + 'invitation.list', + color: Colors.white, + fontSize: 20, + ), + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () { + Navigator.of(context).push(BottomUpPageRoute(InvitationDetail())); + }, + icon: Icon(Icons.add), + label: Text(AppTranslations.of(context).text("invitation.new")), + backgroundColor: primaryColor, + ), + body: new ListView.separated( + separatorBuilder: (context, index) => Divider( + color: Colors.black, + ), + scrollDirection: Axis.vertical, + padding: EdgeInsets.only(left: 15, right: 15, top: 15), + shrinkWrap: true, + itemCount: customerModel.invitations.length, + itemBuilder: (BuildContext context, int index) { + User customer = customerModel.invitations[index]; + return _item(customer); + }), + ), + ); + } + + Widget _item(User customer) { + return Stack( + children: [ + InkWell( + onTap: () { + Navigator.of(context) + .push(BottomUpPageRoute(InvitationEditor(customer: customer))); + }, + child: Row( + children: [ + Expanded( + child: new Padding( + padding: const EdgeInsets.symmetric(vertical: 10.0), + child: new Row( + children: [ + new Padding( + padding: new EdgeInsets.symmetric( + horizontal: 32.0 - dotSize / 2), + child: Icon( + Feather.user, + color: primaryColor, + size: 40, + ), + ), + new Expanded( + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Text( + customer.name, + style: new TextStyle( + fontSize: 15.0, color: primaryColor), + ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: new Text( + customer.phoneNumber, + style: new TextStyle( + fontSize: 15.0, color: Colors.grey), + ), + ), + ], + ), + ), + ], + ), + ), + ), + FlatButton( + onPressed: () => _share(customer), + child: Row( + children: [ + Text("Share"), + Icon(Icons.share), + ], + )), + ], + ), + ), + ], + ); + } + + _share(User user) async { + MainModel mainModel = Provider.of(context, listen: false); + String appUrl = mainModel.setting.appUrl; + final RenderBox box = context.findRenderObject(); + await Share.share( + user.share + "\n Please join us from this link:\n $appUrl", + subject: "Invitation to FCS App", + sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size); + } +} diff --git a/lib/fcs/common/pages/customers/invitation_page.dart b/lib/fcs/common/pages/customers/invitation_page.dart deleted file mode 100644 index 1b93e4f..0000000 --- a/lib/fcs/common/pages/customers/invitation_page.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:fcs/fcs/common/helpers/theme.dart'; -import 'package:fcs/fcs/common/localization/app_translations.dart'; -import 'package:fcs/fcs/common/pages/util.dart'; -import 'package:flutter/material.dart'; -import 'package:fcs/widget/progress.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; - -class InvitationPage extends StatefulWidget { - @override - _InvitationPageState createState() => _InvitationPageState(); -} - -class _InvitationPageState extends State { - TextEditingController _nameController = new TextEditingController(); - TextEditingController _phoneController = new TextEditingController(); - - bool _isLoading = false; - - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return LocalProgress( - inAsyncCall: _isLoading, - child: Scaffold( - appBar: AppBar( - centerTitle: true, - leading: new IconButton( - icon: new Icon( - Icons.close, - ), - onPressed: () => Navigator.of(context).pop(), - ), - backgroundColor: primaryColor, - title: Text(AppTranslations.of(context).text("customer.form.title")), - ), - body: Container( - padding: EdgeInsets.all(18), - child: Column( - children: [ - Expanded( - child: ListView( - children: [ - fcsInput("Name", Icons.person, controller: _nameController), - fcsInput("Phone Number", Icons.phone, - controller: _phoneController), - SizedBox(height: 30), - ], - ), - ), - fcsButton(context, "Invite", callack: () {}), - SizedBox(height: 10) - ], - ), - ), - ), - ); - } -} diff --git a/lib/fcs/common/pages/customers/model/customer_model.dart b/lib/fcs/common/pages/customers/model/customer_model.dart index 57b9771..0277621 100644 --- a/lib/fcs/common/pages/customers/model/customer_model.dart +++ b/lib/fcs/common/pages/customers/model/customer_model.dart @@ -1,26 +1,20 @@ -import 'package:fcs/fcs/common/domain/entities/customer.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:fcs/fcs/common/domain/constants.dart'; +import 'package:fcs/fcs/common/domain/entities/user.dart'; import 'package:fcs/fcs/common/pages/model/base_model.dart'; +import 'package:fcs/fcs/common/services/services.dart'; import 'package:logging/logging.dart'; - class CustomerModel extends BaseModel { final log = Logger('CustomerModel'); - List customers = [ - Customer( - name: 'Ko Nyi', - phoneNumber: '+95 9 717273634', - status: 'Invited' - ), - Customer(name: 'Ko Phyu', phoneNumber: '+1 (939) 382-3844',status: 'Signin'), - Customer(name: 'Ko Ye', phoneNumber: '+95 9 983734783', status: 'Invited'), - ]; - - + List customers = []; + List invitations = []; void initUser(user) async { super.initUser(user); - // _loadCustomer(); + _loadCustomer(); + _loadInvitations(); } @override @@ -28,31 +22,64 @@ class CustomerModel extends BaseModel { customers = []; } - // Future _loadCustomer() async { - // if (!user.isOwnerAndAbove() && !user.hasAccount()) { - // return; - // } + Future inviteUser(String userName, String phoneNumber) { + return Services.instance.userService.inviteUser(userName, phoneNumber); + } - // try { - // Firestore.instance - // .collection("/$biz_collection/${setting.okEnergyId}/$user_collection") - // .where("is_employee", isEqualTo: true) - // .snapshots() - // .listen((QuerySnapshot snapshot) { - // customers.clear(); - // customers = snapshot.documents.map((documentSnapshot) { - // var user = - // User.fromMap(documentSnapshot.data, documentSnapshot.documentID); - // return user; - // }).toList(); - // notifyListeners(); - // }).onError((e) { - // log.warning("Error! $e"); - // }); - // } catch (e) { - // log.warning("Error!! $e"); - // } - // } + Future deleteInvite(String phoneNumber) { + return Services.instance.userService.deleteInvite(phoneNumber); + } + + Future acceptRequest(String userID) { + return Services.instance.userService.acceptRequest(userID); + } + + Future _loadCustomer() async { + if (user == null || !user.hasCustomers()) return; + + try { + Firestore.instance + .collection("/$user_collection") + .where("is_sys_admin", isEqualTo: false) + .snapshots() + .listen((QuerySnapshot snapshot) { + customers.clear(); + customers = snapshot.documents.map((documentSnapshot) { + var user = + User.fromMap(documentSnapshot.data, documentSnapshot.documentID); + return user; + }).toList(); + notifyListeners(); + }).onError((e) { + log.warning("Error! $e"); + }); + } catch (e) { + log.warning("Error!! $e"); + } + } + + Future _loadInvitations() async { + if (user == null || !user.hasCustomers()) return; + + try { + Firestore.instance + .collection("/$invitations_collection") + .snapshots() + .listen((QuerySnapshot snapshot) { + invitations.clear(); + invitations = snapshot.documents.map((documentSnapshot) { + var user = + User.fromMap(documentSnapshot.data, documentSnapshot.documentID); + return user; + }).toList(); + notifyListeners(); + }).onError((e) { + log.warning("Error! $e"); + }); + } catch (e) { + log.warning("Error!! $e"); + } + } // Future updatePrivileges(String userID, List privileges) async { // try { diff --git a/lib/fcs/common/pages/faq/faq_page.dart b/lib/fcs/common/pages/faq/faq_list_page.dart similarity index 78% rename from lib/fcs/common/pages/faq/faq_page.dart rename to lib/fcs/common/pages/faq/faq_list_page.dart index 0ffbde2..0934b90 100644 --- a/lib/fcs/common/pages/faq/faq_page.dart +++ b/lib/fcs/common/pages/faq/faq_list_page.dart @@ -12,12 +12,12 @@ import 'package:provider/provider.dart'; import 'model/faq_model.dart'; -class FAQPage extends StatefulWidget { +class FAQListPage extends StatefulWidget { @override - _FAQPageState createState() => _FAQPageState(); + _FAQListPageState createState() => _FAQListPageState(); } -class _FAQPageState extends State { +class _FAQListPageState extends State { @override void initState() { super.initState(); @@ -51,18 +51,20 @@ class _FAQPageState extends State { color: Colors.white, )), ), - actions:isEditable? [ - IconButton( - onPressed: () => - Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => FAQEditor(), - )), - icon: Icon( - CupertinoIcons.add, - color: Colors.white, - size: 35, - )) - ]:[], + actions: isEditable + ? [ + IconButton( + onPressed: () => + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => FAQEditor(), + )), + icon: Icon( + CupertinoIcons.add, + color: Colors.white, + size: 35, + )) + ] + : [], ), SliverList( delegate: SliverChildBuilderDelegate( @@ -92,15 +94,15 @@ class _FAQPageState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ Flexible( - child: TextLocalStyle( + child: TextLocalStyle( context, faq.question(isEng), - fontSize: 18, + fontSize: 16, ), ), // Spacer(), Padding( - padding: const EdgeInsets.only(left:18.0), + padding: const EdgeInsets.only(left: 18.0), child: Icon( CupertinoIcons.right_chevron, color: primaryColor, diff --git a/lib/fcs/common/pages/home_page.dart b/lib/fcs/common/pages/home_page.dart index 43183a7..3b85b77 100644 --- a/lib/fcs/common/pages/home_page.dart +++ b/lib/fcs/common/pages/home_page.dart @@ -1,8 +1,11 @@ +import 'package:fcs/fcs/common/domain/entities/user.dart'; import 'package:fcs/fcs/common/localization/transalation.dart'; import 'package:fcs/fcs/common/pages/customers/customer_list.dart'; -import 'package:fcs/fcs/common/pages/faq/faq_page.dart'; +import 'package:fcs/fcs/common/pages/faq/faq_list_page.dart'; import 'package:fcs/fcs/common/pages/model/language_model.dart'; import 'package:fcs/fcs/common/pages/model/main_model.dart'; +import 'package:fcs/fcs/common/pages/staff/staff_list.dart'; +import 'package:fcs/fcs/common/pages/util.dart'; import 'package:fcs/fcs/common/pages/widgets/action_button.dart'; import 'package:fcs/fcs/common/pages/widgets/bottom_widgets.dart'; import 'package:fcs/pages/discount_list.dart'; @@ -30,9 +33,8 @@ import '../../../pages/fcs_profile_page.dart'; import '../../../pages/invoice/invoce_list.dart'; import '../../../pages/pickup_list.dart'; import '../../../pages/shipment_rates.dart'; -import '../../../pages/staff_list.dart'; import '../helpers/theme.dart'; -import 'profile_page.dart'; +import 'profile/profile_page.dart'; import 'signin/signin_page.dart'; final msgLog = Logger('backgroundMessageHandler'); @@ -70,6 +72,7 @@ class _HomePageState extends State { @override Widget build(BuildContext context) { + User user = Provider.of(context).user; login = Provider.of(context).isLogin(); // var owner =true;// Provider.of(context).isOwner(); var customer = Provider.of(context).isCustomer(); @@ -78,7 +81,7 @@ class _HomePageState extends State { final faqBtn = _buildBtn("faq.btn", icon: MaterialCommunityIcons.frequently_asked_questions, btnCallback: () => Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => FAQPage(), + builder: (context) => FAQListPage(), ))); final packagesBtn = _buildBtn("package.name", @@ -169,22 +172,23 @@ class _HomePageState extends State { List widgets = []; widgets.add(faqBtn); - // customer ? widgets.add(buyingBtn) : ""; - // customer || owner ? widgets.add(pickUpBtn) : ""; - !customer ? widgets.add(shipmentBtn) : ""; - // customer || owner ? widgets.add(notiBtn) : ""; - // owner ? widgets.add(staffBtn) : ""; - // owner ? widgets.add(fcsProfileBtn) : ""; - // widgets.add(shipmentCostBtn); - // customer || owner ? widgets.add(packagesBtn) : ""; - // owner ? widgets.add(boxesBtn) : ""; - // owner ? widgets.add(deliveryBtn) : ""; - widgets.add(customersBtn); - // customer || owner ? widgets.add(invoicesBtn) : ""; - // owner ? widgets.add(paymentMethodBtn) : ""; - // owner ? widgets.add(discountBtn) : ""; - // widgets.add(termBtn); - + if (user != null) { + // customer ? widgets.add(buyingBtn) : ""; + // customer || owner ? widgets.add(pickUpBtn) : ""; + // !customer ? widgets.add(shipmentBtn) : ""; + // customer || owner ? widgets.add(notiBtn) : ""; + user.hasStaffs() ? widgets.add(staffBtn) : ""; + // owner ? widgets.add(fcsProfileBtn) : ""; + // widgets.add(shipmentCostBtn); + // customer || owner ? widgets.add(packagesBtn) : ""; + // owner ? widgets.add(boxesBtn) : ""; + // owner ? widgets.add(deliveryBtn) : ""; + user.hasCustomers() ? widgets.add(customersBtn) : ""; + // customer || owner ? widgets.add(invoicesBtn) : ""; + // owner ? widgets.add(paymentMethodBtn) : ""; + // owner ? widgets.add(discountBtn) : ""; + // widgets.add(termBtn); + } return OfflineRedirect( child: FlavorBanner( child: Scaffold( @@ -295,6 +299,16 @@ class _HomePageState extends State { ), child: Column( children: [ + user.requested + ? Container( + padding: const EdgeInsets.all(8.0), + child: Text( + getLocalString( + context, "home.invitation.request.msg"), + style: TextStyle(color: Colors.white70), + ), + ) + : Container(), Expanded( child: ListView(children: [ Wrap( diff --git a/lib/fcs/common/pages/initial_language_selection.dart b/lib/fcs/common/pages/initial_language_selection.dart index 375b8fe..9ec4240 100644 --- a/lib/fcs/common/pages/initial_language_selection.dart +++ b/lib/fcs/common/pages/initial_language_selection.dart @@ -5,6 +5,7 @@ import 'package:fcs/fcs/common/pages/model/language_model.dart'; import 'package:fcs/fcs/common/pages/model/main_model.dart'; import 'package:fcs/fcs/common/pages/signin/signin_page.dart'; import 'package:fcs/fcs/common/pages/widgets/local_text.dart'; +import 'package:fcs/fcs/common/pages/widgets/progress.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; @@ -28,10 +29,12 @@ class _InitialLanguageSelectionPageState String selectedLanguage; int selectedIndex; + bool _isLoading; @override void initState() { super.initState(); + _isLoading = false; var languageModel = Provider.of(context, listen: false); this.selectedIndex = languageModel.isEng ? 0 : 1; loadLaunguage(languageModel); @@ -48,119 +51,122 @@ class _InitialLanguageSelectionPageState @override Widget build(BuildContext context) { - return Material( - type: MaterialType.transparency, - child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [Color(0xff272282), primaryColor], - begin: const FractionalOffset(0.8, 0.9), - end: const FractionalOffset(0.9, 0.0), - stops: [0.0, 1.0], + return LocalProgress( + inAsyncCall: _isLoading, + child: Material( + type: MaterialType.transparency, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Color(0xff272282), primaryColor], + begin: const FractionalOffset(0.8, 0.9), + end: const FractionalOffset(0.9, 0.0), + stops: [0.0, 1.0], + ), ), - ), - child: Align( - alignment: Alignment.center, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - children: [ - Container( - height: 40, - child: LocalText(context, "language.selection.title", - fontSize: 20, - fontWeight: FontWeight.w200, - color: Colors.white), - ), - Container( - padding: EdgeInsets.only(top: 0), - child: Card( - color: Color(0xfff4edec), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - child: Container( - padding: EdgeInsets.only(top: 20), - width: 300, - height: 160, - child: Column( - children: languagesList.asMap().entries.map((e) { - var language = e.value; - var key = e.key; - return InkWell( - onTap: () { - _select(key, language); - }, - child: Container( - padding: EdgeInsets.all(2), - decoration: key == languagesList.length - 1 - ? BoxDecoration() - : BoxDecoration( - border: Border( - bottom: - BorderSide(color: Colors.grey[300]), - ), - ), - child: ListTile( - leading: language == 'English' - ? Container( - child: CircleAvatar( - radius: 20, - backgroundImage: AssetImage( - "icons/flags/png/gb.png", - package: 'country_icons', - ), - ), - ) - : Container( - child: CircleAvatar( - radius: 20, - backgroundImage: AssetImage( - "icons/flags/png/mm.png", - package: 'country_icons', - ), - ), - ), - title: Text("$language"), - trailing: Theme( - data: Theme.of(context).copyWith( - unselectedWidgetColor: Colors.grey[400], - ), - child: Radio( - value: key, - groupValue: selectedIndex, - onChanged: (int i) => - _select(key, language), - activeColor: primaryColor, - ), - )), - ), - ); - }).toList()), - ), + child: Align( + alignment: Alignment.center, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + height: 40, + child: LocalText(context, "language.selection.title", + fontSize: 20, + fontWeight: FontWeight.w200, + color: Colors.white), ), - ), - SizedBox(height: 20.0), - Container( - padding: EdgeInsets.only(left: 230, top: 20), - child: Container( - width: 50, - height: 50, - child: InkWell( - onTap: () { - _next(); - }, - child: CircleAvatar( - radius: 25, - backgroundColor: Colors.white, - child: Center( - child: Icon(FontAwesomeIcons.arrowRight, - color: Colors.black87)), + Container( + padding: EdgeInsets.only(top: 0), + child: Card( + color: Color(0xfff4edec), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + child: Container( + padding: EdgeInsets.only(top: 20), + width: 300, + height: 160, + child: Column( + children: languagesList.asMap().entries.map((e) { + var language = e.value; + var key = e.key; + return InkWell( + onTap: () { + _select(key, language); + }, + child: Container( + padding: EdgeInsets.all(2), + decoration: key == languagesList.length - 1 + ? BoxDecoration() + : BoxDecoration( + border: Border( + bottom: + BorderSide(color: Colors.grey[300]), + ), + ), + child: ListTile( + leading: language == 'English' + ? Container( + child: CircleAvatar( + radius: 20, + backgroundImage: AssetImage( + "icons/flags/png/gb.png", + package: 'country_icons', + ), + ), + ) + : Container( + child: CircleAvatar( + radius: 20, + backgroundImage: AssetImage( + "icons/flags/png/mm.png", + package: 'country_icons', + ), + ), + ), + title: Text("$language"), + trailing: Theme( + data: Theme.of(context).copyWith( + unselectedWidgetColor: Colors.grey[400], + ), + child: Radio( + value: key, + groupValue: selectedIndex, + onChanged: (int i) => + _select(key, language), + activeColor: primaryColor, + ), + )), + ), + ); + }).toList()), ), ), ), - ) - ], + SizedBox(height: 20.0), + Container( + padding: EdgeInsets.only(left: 230, top: 20), + child: Container( + width: 50, + height: 50, + child: InkWell( + onTap: () { + _next(); + }, + child: CircleAvatar( + radius: 25, + backgroundColor: Colors.white, + child: Center( + child: Icon(FontAwesomeIcons.arrowRight, + color: Colors.black87)), + ), + ), + ), + ) + ], + ), ), ), ), @@ -179,9 +185,18 @@ class _InitialLanguageSelectionPageState } _next() { - SharedPref.finishFirstLaunch(); - bool isLogin = Provider.of(context, listen: false).isLogin(); - String page = isLogin ? "/home" : "/welcome"; - Navigator.of(context).pushReplacementNamed(page); + setState(() { + _isLoading = true; + }); + try { + SharedPref.finishFirstLaunch(); + bool isLogin = Provider.of(context, listen: false).isLogin(); + String page = isLogin ? "/home" : "/welcome"; + Navigator.of(context).pushReplacementNamed(page); + } catch (e) {} finally { + setState(() { + _isLoading = false; + }); + } } } diff --git a/lib/fcs/common/pages/model/main_model.dart b/lib/fcs/common/pages/model/main_model.dart index a730d1a..85bc6fb 100644 --- a/lib/fcs/common/pages/model/main_model.dart +++ b/lib/fcs/common/pages/model/main_model.dart @@ -36,20 +36,21 @@ class MainModel extends ChangeNotifier { }); Services.instance.authService.onAuthStatus().listen((event) { this.user = event; + _initUser(user); notifyListeners(); }); } bool faqEditable() { - return this.user != null && false; + return this.user != null && this.user.hasSupport(); } bool termEditable() { - return this.user != null && false; + return this.user != null && this.user.hasSupport(); } bool contactEditable() { - return this.user != null && false; + return this.user != null && this.user.hasSupport(); } bool isLogin() { @@ -81,13 +82,13 @@ class MainModel extends ChangeNotifier { models.add(model); } - // void _initUser(User user) { - // models.forEach((m) => m.initUser(user)); + void _initUser(User user) { + models.forEach((m) => m.initUser(user)); - // if (firebaseMessaging != null) { - // firebaseMessaging.subscribeToTopic(user.docID); - // } - // } + // if (firebaseMessaging != null) { + // firebaseMessaging.subscribeToTopic(user.docID); + // } + } void _initSetting(Setting setting) { models.forEach((m) => m.initSetting(setting)); @@ -106,6 +107,7 @@ class MainModel extends ChangeNotifier { void _loadUser() async { try { this.user = await Services.instance.authService.getUser(); + _initUser(user); } finally { this.isLoaded = true; notifyListeners(); @@ -143,13 +145,27 @@ class MainModel extends ChangeNotifier { Future signout() { this.user = null; + // logout models + models.forEach((m) => m.logout()); notifyListeners(); return Services.instance.authService.signout(); } + Future hasInvite() async { + return Services.instance.authService.hasInvite(); + } + Future signup(String userName) async { await Services.instance.authService.signup(userName); - this.user = await Services.instance.authService.getUser(); + this.user = + await Services.instance.authService.getUser(refreshIdToken: true); + notifyListeners(); + } + + Future updateProfile(String newUserName) async { + await Services.instance.authService.updateProfile(newUserName); + this.user = + await Services.instance.authService.getUser(refreshIdToken: true); notifyListeners(); } } diff --git a/lib/fcs/common/pages/profile/profile_edit.dart b/lib/fcs/common/pages/profile/profile_edit.dart new file mode 100644 index 0000000..f6f1fd7 --- /dev/null +++ b/lib/fcs/common/pages/profile/profile_edit.dart @@ -0,0 +1,106 @@ +import 'package:fcs/fcs/common/helpers/theme.dart'; +import 'package:fcs/fcs/common/localization/app_translations.dart'; +import 'package:fcs/fcs/common/pages/model/language_model.dart'; +import 'package:fcs/fcs/common/pages/model/main_model.dart'; +import 'package:fcs/fcs/common/pages/widgets/progress.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:fcs/fcs/common/pages/util.dart'; + +typedef void ProfileCallback(); + +class ProfileEdit extends StatefulWidget { + @override + _ProfileEditState createState() => _ProfileEditState(); +} + +class _ProfileEditState extends State { + final TextEditingController nameController = new TextEditingController(); + bool _loading = false; + + @override + void initState() { + super.initState(); + MainModel mainModel = Provider.of(context, listen: false); + nameController.text = mainModel.user.name; + } + + @override + Widget build(BuildContext context) { + var languageModel = Provider.of(context); + + final name = Container( + padding: EdgeInsets.only(top: 0, left: 20, right: 15, bottom: 30), + child: TextFormField( + controller: nameController, + autofocus: true, + cursorColor: primaryColor, + style: textStyle, + decoration: new InputDecoration( + labelText: AppTranslations.of(context).text("profile.name"), + labelStyle: languageModel.isEng ? labelStyle : labelStyleMM, + icon: Icon( + Icons.person, + color: primaryColor, + ), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: primaryColor, width: 1.0)), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: primaryColor, width: 1.0)), + ), + )); + + final saveBtn = + fcsButton(context, getLocalString(context, "btn.save"), callack: _save); + + return LocalProgress( + inAsyncCall: _loading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + title: Text( + AppTranslations.of(context).text("profile.edit_title"), + style: TextStyle(color: primaryColor), + ), + backgroundColor: Colors.white, + shadowColor: Colors.transparent, + leading: IconButton( + icon: Icon( + CupertinoIcons.back, + size: 35, + color: primaryColor, + ), + onPressed: () => Navigator.of(context).pop(), + ), + ), + body: Column( + children: [ + Expanded(child: name), + Padding( + padding: const EdgeInsets.all(18.0), + child: saveBtn, + ), + ], + ), + ), + ); + } + + _save() async { + setState(() { + _loading = true; + }); + try { + await Provider.of(context, listen: false) + .updateProfile(nameController.text); + Navigator.pop(context); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _loading = false; + }); + } + } +} diff --git a/lib/fcs/common/pages/profile_page.dart b/lib/fcs/common/pages/profile/profile_page.dart similarity index 52% rename from lib/fcs/common/pages/profile_page.dart rename to lib/fcs/common/pages/profile/profile_page.dart index 13e45c2..b056cb9 100644 --- a/lib/fcs/common/pages/profile_page.dart +++ b/lib/fcs/common/pages/profile/profile_page.dart @@ -2,6 +2,8 @@ import 'package:fcs/fcs/common/localization/app_translations.dart'; import 'package:fcs/fcs/common/localization/transalation.dart'; import 'package:fcs/fcs/common/pages/model/language_model.dart'; import 'package:fcs/fcs/common/pages/model/main_model.dart'; +import 'package:fcs/fcs/common/pages/profile/profile_edit.dart'; +import 'package:fcs/fcs/common/pages/widgets/display_text.dart'; import 'package:fcs/fcs/common/pages/widgets/progress.dart'; import 'package:fcs/model/shipment_model.dart'; import 'package:fcs/model/user_model.dart'; @@ -12,10 +14,11 @@ import 'package:fcs/vo/shipping_address.dart'; import 'package:fcs/widget/bottom_up_page_route.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; -import '../../../pages/shipping_address_editor.dart'; -import '../helpers/theme.dart'; +import '../../../../pages/shipping_address_editor.dart'; +import '../../helpers/theme.dart'; typedef void ProfileCallback(); @@ -25,6 +28,7 @@ class Profile extends StatefulWidget { } class _ProfileState extends State { + GlobalKey key = GlobalKey(); bool _isLoading = false; String selectedLanguage; TextEditingController bizNameController = new TextEditingController(); @@ -49,180 +53,112 @@ class _ProfileState extends State { @override Widget build(BuildContext context) { - var languageModel = Provider.of(context); MainModel mainModel = Provider.of(context); - final namebox = Container( - padding: EdgeInsets.only(top: 10, left: 25.0, right: 25.0), - child: Container( - height: 45.0, - child: Row( - children: [ - Padding( - padding: EdgeInsets.only(left: 0.0), - child: Text( - AppTranslations.of(context).text("profile.name"), - style: languageModel.isEng - ? TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal) - : TextStyle( - fontSize: 15.0, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal, - fontFamily: "MyanmarUnicode"), - ), - ), - SizedBox( - width: 30, - ), - Container( - child: Center( - child: Text( - mainModel.user == null || mainModel.user.name == null - ? "" - : mainModel.user.name, - style: - TextStyle(fontSize: 16.0, fontStyle: FontStyle.normal), - ), - ), - ) - ], - ), - )); - - final phonenumberbox = Container( - padding: EdgeInsets.only(left: 25.0, right: 25.0), - height: 45.0, - child: Row( - children: [ - Padding( - padding: EdgeInsets.only(left: 0.0), - child: Text( - AppTranslations.of(context).text("profile.phone"), - style: languageModel.isEng - ? TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal) - : TextStyle( - fontSize: 15.0, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal, - fontFamily: "MyanmarUnicode"), - ), - ), - SizedBox( - width: 27, - ), - Container( - child: Center( - child: Text( - mainModel.user == null - ? "" - : mainModel.user.phone == null ? '' : mainModel.user.phone, - style: TextStyle(fontSize: 16.0, fontStyle: FontStyle.normal), - ), - ), - ) - ], - ), + final namebox = DisplayText( + text: mainModel.user.name, + labelText: getLocalString(context, "profile.name"), + iconData: Icons.person, ); - final logoutbutton = Container( - padding: EdgeInsets.only(left: 20.0, right: 24.0), - child: Padding( - padding: EdgeInsets.symmetric(vertical: 10.0), - child: Card( - elevation: 23, - child: Container( - height: 45.0, - child: ButtonTheme( - minWidth: 900.0, - height: 100.0, - child: FlatButton.icon( - onPressed: () { - showConfirmDialog(context, "profile.logout.confirm", - () async { - setState(() { - _isLoading = true; - }); - await context.read().signout(); - Navigator.of(context).pushNamedAndRemoveUntil( - "/welcome", ModalRoute.withName('/welcome')); - Future.delayed(Duration(seconds: 1), () { - if (mounted) { - setState(() { - _isLoading = false; - }); - } - }); - }); - }, - label: Text( - AppTranslations.of(context).text("profile.logout"), - style: languageModel.isEng - ? TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal) - : TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal, - fontFamily: "MyanmarUnicode")), - icon: Icon( - Icons.exit_to_app, - ), - ), - ), - ), - ))); + final phonenumberbox = DisplayText( + text: mainModel.user.phone, + labelText: getLocalString(context, "profile.phone"), + iconData: Icons.phone, + ); + final fcsIDBox = Row( + children: [ + Expanded( + child: DisplayText( + text: mainModel.user.fcsID, + labelText: getLocalString(context, "customer.fcs.id"), + iconData: Icons.account_circle, + ), + ), + IconButton( + icon: Icon(Icons.content_copy, color: Colors.grey), + onPressed: () => _copy( + getLocalString(context, "customer.fcs.id"), mainModel.user.fcsID), + ) + ], + ); + final usaShippingAddressBox = Row( + children: [ + Expanded( + child: DisplayText( + text: mainModel.setting.usaAddress, + labelText: getLocalString(context, "profile.usa.shipping.address"), + iconData: Icons.location_on, + ), + ), + IconButton( + icon: Icon(Icons.content_copy, color: Colors.grey), + onPressed: () => _copy( + getLocalString(context, "profile.usa.shipping.address"), + mainModel.setting.usaAddress), + ) + ], + ); + + final logoutbutton = fcsButton( + context, getLocalString(context, "profile.logout"), + callack: _logout, iconData: Icons.exit_to_app); + return LocalProgress( inAsyncCall: _isLoading, child: Scaffold( + key: key, appBar: AppBar( + centerTitle: true, leading: IconButton( icon: Icon( CupertinoIcons.back, - size: 30, + size: 35, + color: primaryColor, ), onPressed: () => Navigator.of(context).pop(), ), title: Text( AppTranslations.of(context).text("profile.title"), + style: TextStyle(color: primaryColor), ), - backgroundColor: primaryColor, + shadowColor: Colors.transparent, + backgroundColor: Colors.white, actions: [], ), - body: Column( - children: [ - Expanded( - child: ListView( - // padding: EdgeInsets.only(left: 25.0, right: 25.0), - shrinkWrap: true, - children: [ - Row( - children: [ - namebox, - Padding( - padding: const EdgeInsets.only(left: 18.0), - child: Icon(Icons.edit, color: primaryColor), - ) - ], - ), - mainModel.isCustomer() - ? Container() - : getPrivilegeBox(context), - phonenumberbox, - // getShippingAddressList(context), - ], + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Expanded( + child: ListView( + shrinkWrap: true, + children: [ + Row( + children: [ + Expanded(child: namebox), + Padding( + padding: const EdgeInsets.only(right: 0), + child: IconButton( + icon: Icon(Icons.edit, color: Colors.grey), + onPressed: _editName), + ) + ], + ), + // mainModel.isCustomer() + // ? Container() + // : getPrivilegeBox(context), + phonenumberbox, + fcsIDBox, + usaShippingAddressBox, + // getShippingAddressList(context), + ], + ), ), - ), - logoutbutton, - SizedBox(height: 25) - ], + logoutbutton, + SizedBox(height: 25) + ], + ), ), ), ); @@ -347,4 +283,43 @@ class _ProfileState extends State { ); }).toList(); } + + _copy(String title, String data) { + Clipboard.setData(ClipboardData(text: data)); + _showToast(title); + } + + void _showToast(String title) { + final ScaffoldState scaffold = key.currentState; + scaffold.showSnackBar( + SnackBar( + content: Text('copied "$title" data to clipboard'), + backgroundColor: secondaryColor, + duration: Duration(seconds: 1), + ), + ); + } + + _editName() { + Navigator.of(context) + .push(CupertinoPageRoute(builder: (context) => ProfileEdit())); + } + + _logout() { + showConfirmDialog(context, "profile.logout.confirm", () async { + setState(() { + _isLoading = true; + }); + await context.read().signout(); + Navigator.of(context) + .pushNamedAndRemoveUntil("/welcome", ModalRoute.withName('/welcome')); + Future.delayed(Duration(seconds: 1), () { + if (mounted) { + setState(() { + _isLoading = false; + }); + } + }); + }); + } } diff --git a/lib/fcs/common/pages/signin/invitation_request_page.dart b/lib/fcs/common/pages/signin/invitation_request_page.dart index 2a7beb6..e9228d8 100644 --- a/lib/fcs/common/pages/signin/invitation_request_page.dart +++ b/lib/fcs/common/pages/signin/invitation_request_page.dart @@ -1,9 +1,7 @@ import 'package:fcs/fcs/common/pages/model/main_model.dart'; -import 'package:fcs/fcs/common/pages/signin/model/signin_model.dart'; import 'package:fcs/fcs/common/pages/util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; import '../../../../widget/local_text.dart'; @@ -107,16 +105,17 @@ class _RequestInvitationPageState extends State { _isLoading = true; }); try { - await context.read().requestInvitation(nameCtl.text); + // just signup to request for invitation + await context.read().signup(nameCtl.text); await showMsgDialog(context, "Successful", getLocalString(context, "invite.request.successful")); + Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false); } catch (e) { await showMsgDialog(context, "Error", e.toString()); } finally { setState(() { _isLoading = false; }); - Navigator.pushNamedAndRemoveUntil(context, "/welcome", (r) => false); } } } diff --git a/lib/fcs/common/pages/signin/model/signin_model.dart b/lib/fcs/common/pages/signin/model/signin_model.dart deleted file mode 100644 index e47678e..0000000 --- a/lib/fcs/common/pages/signin/model/signin_model.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'dart:async'; - -import 'package:fcs/fcs/common/pages/model/base_model.dart'; -import 'package:fcs/fcs/common/services/services.dart'; -import 'package:logging/logging.dart'; - -class SigninModel extends BaseModel { - final log = Logger('SigninModel'); - - Future checkInvatation() async { - var invited = await request("/check_invitation", "GET", - token: await Services.instance.authService.getToken()); - return invited["invited"]; - } - - Future requestInvitation(String name) async { - await request("/request_invitation", "POST", - payload: {"user_name": name}, - token: await Services.instance.authService.getToken()); - await Services.instance.authService.signout(); - } -} diff --git a/lib/fcs/common/pages/signin/signin_logic.dart b/lib/fcs/common/pages/signin/signin_logic.dart new file mode 100644 index 0000000..6e48b85 --- /dev/null +++ b/lib/fcs/common/pages/signin/signin_logic.dart @@ -0,0 +1,31 @@ +import 'package:fcs/fcs/common/domain/entities/setting.dart'; +import 'package:fcs/fcs/common/domain/entities/user.dart'; +import 'package:fcs/fcs/common/pages/model/main_model.dart'; +import 'package:fcs/fcs/common/pages/signin/invitation_request_page.dart'; +import 'package:fcs/fcs/common/pages/signin/signup_page.dart'; +import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart'; +import 'package:flutter/widgets.dart'; +import 'package:provider/provider.dart'; + +navigateAfterAuthVerified(BuildContext context) async { + User user = Provider.of(context, listen: false).user; + Setting setting = Provider.of(context, listen: false).setting; + + if (user == null || setting == null) return; + + if (user.joined || user.requested) { + Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false); + } else { + if (setting.inviteRequired) { + bool invited = + await Provider.of(context, listen: false).hasInvite(); + if (!invited) { + await Navigator.of(context).pushAndRemoveUntil( + BottomUpPageRoute(RequestInvitationPage()), (r) => false); + return; + } + } + await Navigator.of(context) + .pushAndRemoveUntil(BottomUpPageRoute(SignupPage()), (r) => false); + } +} diff --git a/lib/fcs/common/pages/signin/signin_page.dart b/lib/fcs/common/pages/signin/signin_page.dart index cdf2c2a..28b2d2d 100644 --- a/lib/fcs/common/pages/signin/signin_page.dart +++ b/lib/fcs/common/pages/signin/signin_page.dart @@ -1,8 +1,10 @@ import 'package:country_code_picker/country_code_picker.dart'; import 'package:fcs/fcs/common/domain/entities/auth_result.dart'; import 'package:fcs/fcs/common/domain/entities/auth_status.dart'; +import 'package:fcs/fcs/common/domain/entities/setting.dart'; import 'package:fcs/fcs/common/domain/entities/user.dart'; import 'package:fcs/fcs/common/pages/model/main_model.dart'; +import 'package:fcs/fcs/common/pages/signin/signin_logic.dart'; import 'package:fcs/fcs/common/pages/signin/signup_page.dart'; import 'package:fcs/fcs/common/pages/widgets/local_text.dart'; import 'package:fcs/widget/bottom_up_page_route.dart'; @@ -179,12 +181,7 @@ class _SigninPageState extends State { .push(BottomUpPageRoute(SmsCodePage(phoneNumber: phoneNumber))); Navigator.pop(context); } else if (auth.authStatus == AuthStatus.AUTH_VERIFIED) { - User user = context.read().user; - if (user != null && !user.hasSignup) { - await Navigator.of(context).push(BottomUpPageRoute(SignupPage())); - } else { - Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false); - } + await navigateAfterAuthVerified(context); } if (auth.authStatus == AuthStatus.ERROR) { showMsgDialog(context, "Error", auth.authErrorMsg); diff --git a/lib/fcs/common/pages/signin/signup_page.dart b/lib/fcs/common/pages/signin/signup_page.dart index 4f5b611..4686f80 100644 --- a/lib/fcs/common/pages/signin/signup_page.dart +++ b/lib/fcs/common/pages/signin/signup_page.dart @@ -1,4 +1,5 @@ import 'package:fcs/fcs/common/pages/model/main_model.dart'; +import 'package:fcs/fcs/common/pages/util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -108,7 +109,18 @@ class _SignupPageState extends State { } _submit() async { - await context.read().signup(nameCtl.text); - Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false); + setState(() { + _isLoading = true; + }); + try { + await context.read().signup(nameCtl.text); + Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } } } diff --git a/lib/fcs/common/pages/signin/sms_code_page.dart b/lib/fcs/common/pages/signin/sms_code_page.dart index 756d02b..47e884f 100644 --- a/lib/fcs/common/pages/signin/sms_code_page.dart +++ b/lib/fcs/common/pages/signin/sms_code_page.dart @@ -2,20 +2,16 @@ import 'dart:async'; import 'package:fcs/fcs/common/domain/entities/auth_result.dart'; import 'package:fcs/fcs/common/domain/entities/auth_status.dart'; -import 'package:fcs/fcs/common/domain/entities/user.dart'; import 'package:fcs/fcs/common/pages/model/main_model.dart'; -import 'package:fcs/fcs/common/pages/signin/model/signin_model.dart'; -import 'package:fcs/fcs/common/pages/signin/invitation_request_page.dart'; +import 'package:fcs/fcs/common/pages/signin/signin_logic.dart'; import 'package:fcs/fcs/common/pages/util.dart'; import 'package:fcs/fcs/common/pages/widgets/local_text.dart'; -import 'package:fcs/widget/bottom_up_page_route.dart'; import 'package:flutter/material.dart'; import 'package:pin_input_text_field/pin_input_text_field.dart'; import 'package:provider/provider.dart'; -import 'signup_page.dart'; -import '../../helpers/theme.dart'; import '../../../../widget/progress.dart'; +import '../../helpers/theme.dart'; const resend_count_sec = 30; @@ -202,18 +198,7 @@ class _SmsCodePageState extends State { AuthResult auth = await context.read().signin(this.pin); if (auth.authStatus == AuthStatus.AUTH_VERIFIED) { - User user = context.read().user; - if (user != null && !user.hasSignup) { - bool invited = await context.read().checkInvatation(); - if (invited) { - await Navigator.of(context).push(BottomUpPageRoute(SignupPage())); - } else { - await Navigator.of(context) - .push(BottomUpPageRoute(RequestInvitationPage())); - } - } else { - Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false); - } + await navigateAfterAuthVerified(context); } } catch (e) { showMsgDialog(context, "Error", e.toString()); diff --git a/lib/fcs/common/pages/splash_page.dart b/lib/fcs/common/pages/splash_page.dart index 074db9f..6658f11 100644 --- a/lib/fcs/common/pages/splash_page.dart +++ b/lib/fcs/common/pages/splash_page.dart @@ -52,7 +52,7 @@ class _SplashScreenState extends State { if (mainModel.isLoaded) { if (mainModel.isFirstLaunch) { page = "/language_selection"; - } else if (mainModel.isLogin() && mainModel.user.hasSignup) { + } else if (mainModel.isLogin()) { page = "/home"; } else { page = "/welcome"; diff --git a/lib/fcs/common/pages/staff/model/staff_model.dart b/lib/fcs/common/pages/staff/model/staff_model.dart new file mode 100644 index 0000000..9cdefdd --- /dev/null +++ b/lib/fcs/common/pages/staff/model/staff_model.dart @@ -0,0 +1,88 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:fcs/fcs/common/domain/constants.dart'; +import 'package:fcs/fcs/common/domain/entities/role.dart'; +import 'package:fcs/fcs/common/domain/entities/user.dart'; +import 'package:fcs/fcs/common/helpers/firebase_helper.dart'; +import 'package:fcs/fcs/common/pages/model/base_model.dart'; +import 'package:fcs/fcs/common/services/services.dart'; +import 'package:logging/logging.dart'; + +class StaffModel extends BaseModel { + final log = Logger('StaffModel'); + + List employees = []; + List privileges = []; + + void initUser(user) async { + super.initUser(user); + _loadPrivileges(); + _loadEmployees(); + } + + @override + logout() async { + employees = []; + } + + Future _loadEmployees() async { + if (user == null || !user.hasStaffs()) return; + + try { + Firestore.instance + .collection("/$user_collection") + .where("is_employee", isEqualTo: true) + .where("is_sys_admin", isEqualTo: false) + .snapshots() + .listen((QuerySnapshot snapshot) { + employees.clear(); + employees = snapshot.documents.map((documentSnapshot) { + var user = + User.fromMap(documentSnapshot.data, documentSnapshot.documentID); + return user; + }).toList(); + notifyListeners(); + }).onError((e) { + log.warning("Error! $e"); + }); + } catch (e) { + log.warning("Error!! $e"); + } + } + + Future _loadPrivileges() async { + if (user == null || !user.hasStaffs()) return; + + try { + Firestore.instance + .collection("/$privilege_collection") + .snapshots() + .listen((QuerySnapshot snapshot) { + privileges.clear(); + privileges = snapshot.documents.map((documentSnapshot) { + var privilege = Privilege.fromMap( + documentSnapshot.data, documentSnapshot.documentID); + return privilege; + }).toList(); + notifyListeners(); + }).onError((e) { + log.warning("Error! $e"); + }); + } catch (e) { + log.warning("Error!! $e"); + } + } + + Future updatePrivileges(String userID, List privileges) async { + try { + await request("/employee/privileges", "PUT", + payload: {"id": userID, "privileges": privileges}, + token: await getToken()); + } catch (e) { + throw Exception(e); + } + } + + Future findUser(String phoneNumber) { + return Services.instance.userService.findUser(phoneNumber); + } +} diff --git a/lib/fcs/common/pages/staff/staff_editor.dart b/lib/fcs/common/pages/staff/staff_editor.dart new file mode 100644 index 0000000..cf388ac --- /dev/null +++ b/lib/fcs/common/pages/staff/staff_editor.dart @@ -0,0 +1,276 @@ +import 'package:fcs/fcs/common/domain/entities/role.dart'; +import 'package:fcs/fcs/common/domain/entities/user.dart'; +import 'package:fcs/fcs/common/helpers/theme.dart'; +import 'package:fcs/fcs/common/localization/app_translations.dart'; +import 'package:fcs/fcs/common/pages/model/language_model.dart'; +import 'package:fcs/fcs/common/pages/staff/model/staff_model.dart'; +import 'package:fcs/fcs/common/pages/util.dart'; +import 'package:fcs/fcs/common/pages/widgets/display_text.dart'; +import 'package:fcs/fcs/common/pages/widgets/local_text.dart'; +import 'package:fcs/fcs/common/pages/widgets/progress.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +typedef void FindCallBack(); + +class StaffEditor extends StatefulWidget { + final User staff; + const StaffEditor({this.staff}); + @override + _StaffEditorState createState() => _StaffEditorState(); +} + +class _StaffEditorState extends State { + TextEditingController _phoneInput = new TextEditingController(); + + bool _isLoading = false; + User user; + User selectedUser; + List privileges = []; + bool isNew = true; + + @override + void initState() { + super.initState(); + privileges = Provider.of(context, listen: false).privileges; + isNew = widget.staff == null; + user = User(); + if (!isNew) { + user = + User(name: widget.staff.name, phoneNumber: widget.staff.phoneNumber); + user.privileges = widget.staff.privileges; + privileges.forEach((p) => user.privileges.contains(p.id) + ? p.isChecked = true + : p.isChecked = false); + } else { + user.name = ""; + user.phoneNumber = ""; + privileges.forEach((p) => p.isChecked = false); + } + } + + List showprivilegeList(BuildContext context) { + return privileges.map((p) { + return new ListTile( + title: InkWell( + onTap: () { + setState(() { + p.isChecked = p.isChecked == null ? true : !p.isChecked; + }); + }, + child: new Row( + children: [ + new Checkbox( + value: p.isChecked == null ? false : p.isChecked, + activeColor: primaryColor, + onChanged: (bool value) { + setState(() { + p.isChecked = value; + }); + }), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Text( + p.name, + style: TextStyle( + fontSize: 15.0, + ), + ), + ], + ), + ], + ), + )); + }).toList(); + } + + Widget phoneSearchbox(BuildContext context, FindCallBack findCallBack) { + var languageModel = Provider.of(context); + return Container( + padding: EdgeInsets.only(bottom: 15, left: 8), + child: Stack( + alignment: const Alignment(1.2, 1.0), + children: [ + TextFormField( + controller: _phoneInput, + autofocus: false, + cursorColor: primaryColor, + keyboardType: TextInputType.phone, + style: textStyle, + decoration: new InputDecoration( + labelText: AppTranslations.of(context).text('employee.phone'), + labelStyle: languageModel.isEng ? labelStyle : labelStyleMM, + icon: Icon( + Icons.phone, + color: primaryColor, + ), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: primaryColor, width: 1.0)), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: primaryColor, width: 1.0)), + ), + ), + new FlatButton( + onPressed: () { + findCallBack(); + }, + child: new Icon( + Icons.search, + size: 25, + )) + ], + )); + } + + @override + Widget build(BuildContext context) { + final namebox = DisplayText( + text: user.name, + labelText: getLocalString(context, "customer.name"), + iconData: Icons.person, + ); + var phoneNumberBox = Row( + children: [ + Expanded( + child: DisplayText( + text: user.phoneNumber, + labelText: getLocalString(context, "customer.phone"), + iconData: Icons.phone, + )), + IconButton( + icon: Icon(Icons.open_in_new, color: primaryColor), + onPressed: () => call(context, user.phoneNumber)), + ], + ); + + final updateButton = fcsButton( + context, + getLocalString(context, 'staff.update'), + callack: _save, + ); + final addButton = fcsButton( + context, + getLocalString(context, 'staff.add'), + callack: _add, + ); + + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: new Icon(Icons.close, color: primaryColor, size: 30), + onPressed: () => Navigator.of(context).pop(), + ), + shadowColor: Colors.transparent, + backgroundColor: Colors.white, + title: LocalText( + context, + "staff.form.title", + fontSize: 20, + color: primaryColor, + ), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + isNew + ? phoneSearchbox(context, () => _findUser(context)) + : Container(), + phoneNumberBox, + namebox, + Expanded( + child: ListView( + shrinkWrap: true, + padding: EdgeInsets.only(left: 24.0, right: 24.0), + children: showprivilegeList(context), + ), + ), + Container( + child: isNew ? addButton : updateButton, + ), + SizedBox( + height: 10, + ) + ], + ), + ), + )); + } + + _add() async { + if (isNew && selectedUser == null) { + showMsgDialog(context, "Error", "Invalid user!"); + return; + } + setState(() { + _isLoading = true; + }); + StaffModel staffModel = Provider.of(context, listen: false); + try { + await staffModel.updatePrivileges(this.selectedUser.id, privilegesIDs()); + Navigator.pop(context); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } + + List privilegesIDs() { + return this.privileges.where((p) => p.isChecked).map((p) => p.id).toList(); + } + + _save() async { + setState(() { + _isLoading = true; + }); + if (widget.staff == null) return; + StaffModel staffModel = Provider.of(context, listen: false); + try { + await staffModel.updatePrivileges(widget.staff.id, privilegesIDs()); + Navigator.pop(context); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } + + _findUser(BuildContext context) async { + StaffModel staffModel = Provider.of(context, listen: false); + + setState(() { + _isLoading = true; + }); + try { + User _user = await staffModel.findUser(_phoneInput.text); + if (_user == null) { + showMsgDialog(context, "Error", _phoneInput.text + " not found!"); + return; + } + this.selectedUser = _user; + this.user = _user; + setState(() { + if (user.privileges != null) { + privileges.forEach((p) => user.privileges.contains(p.id) + ? p.isChecked = true + : p.isChecked = false); + } + }); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } +} diff --git a/lib/fcs/common/pages/staff/staff_list.dart b/lib/fcs/common/pages/staff/staff_list.dart new file mode 100644 index 0000000..3be1d78 --- /dev/null +++ b/lib/fcs/common/pages/staff/staff_list.dart @@ -0,0 +1,124 @@ +import 'package:fcs/fcs/common/domain/entities/user.dart'; +import 'package:fcs/fcs/common/localization/app_translations.dart'; +import 'package:fcs/fcs/common/pages/staff/model/staff_model.dart'; +import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart'; +import 'package:fcs/fcs/common/pages/widgets/local_text.dart'; +import 'package:fcs/fcs/common/helpers/theme.dart'; +import 'package:fcs/fcs/common/pages/widgets/progress.dart'; + +import 'package:flutter/material.dart'; +import 'package:flutter_icons/flutter_icons.dart'; +import 'package:intl/intl.dart'; + +import 'package:provider/provider.dart'; +import 'staff_editor.dart'; + +class StaffList extends StatefulWidget { + @override + _StaffListState createState() => _StaffListState(); +} + +class _StaffListState extends State { + var dateFormatter = new DateFormat('dd MMM yyyy - hh:mm:ss a'); + final double dotSize = 15.0; + bool _isLoading = false; + + @override + Widget build(BuildContext context) { + StaffModel staffModel = Provider.of(context); + + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + appBar: AppBar( + centerTitle: true, + leading: new IconButton( + icon: new Icon(Icons.close), + onPressed: () => Navigator.of(context).pop(), + ), + backgroundColor: primaryColor, + title: LocalText( + context, + 'staff.list.title', + color: Colors.white, + fontSize: 20, + ), + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () { + Navigator.of(context).push(BottomUpPageRoute(StaffEditor())); + }, + icon: Icon(Icons.add), + label: Text(AppTranslations.of(context).text("staff.new")), + backgroundColor: primaryColor, + ), + body: new ListView.separated( + separatorBuilder: (context, index) => Divider( + color: Colors.black, + ), + scrollDirection: Axis.vertical, + padding: EdgeInsets.only(left: 5, right: 5, top: 5), + shrinkWrap: true, + itemCount: staffModel.employees.length, + itemBuilder: (BuildContext context, int index) { + User user = staffModel.employees[index]; + return _item(user); + }), + ), + ); + } + + Widget _item(User user) { + return Stack( + children: [ + InkWell( + onTap: () { + Navigator.of(context) + .push(BottomUpPageRoute(StaffEditor(staff: user))); + }, + child: Row( + children: [ + Expanded( + child: new Padding( + padding: const EdgeInsets.symmetric(vertical: 10.0), + child: new Row( + children: [ + new Padding( + padding: new EdgeInsets.symmetric( + horizontal: 32.0 - dotSize / 2), + child: Icon( + MaterialCommunityIcons.worker, + color: primaryColor, + size: 40, + ), + ), + new Expanded( + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Text( + user.name, + style: new TextStyle(fontSize: 15.0), + ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: new Text( + user.phoneNumber, + style: new TextStyle( + fontSize: 15.0, color: Colors.grey), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/fcs/common/pages/util.dart b/lib/fcs/common/pages/util.dart index 20c4957..1736bfc 100644 --- a/lib/fcs/common/pages/util.dart +++ b/lib/fcs/common/pages/util.dart @@ -410,13 +410,18 @@ Widget phoneWidget(BuildContext context, String phone) { } Widget fcsInput(String label, IconData iconData, - {TextEditingController controller, String value}) { + {TextEditingController controller, + String value, + bool autoFocus = false, + TextInputType textInputType}) { return TextFormField( initialValue: value, controller: controller, cursorColor: primaryColor, maxLines: null, minLines: 1, + autofocus: autoFocus, + keyboardType: textInputType, decoration: InputDecoration( fillColor: Colors.white, labelText: label, @@ -553,7 +558,19 @@ Widget _dropDown() { ); } -Widget fcsButton(BuildContext context, String text, {Function callack}) { +Widget fcsButton(BuildContext context, String text, + {Function callack, IconData iconData}) { + var languageModel = Provider.of(context); + + var style = languageModel.isEng + ? TextStyle( + fontSize: 16.0, color: Colors.white, fontWeight: FontWeight.bold) + : TextStyle( + fontSize: 16.0, + color: Colors.white, + fontWeight: FontWeight.bold, + fontFamily: "Myanmar3"); + return Container( padding: EdgeInsets.only(left: 10, right: 10, top: 10), child: Container( @@ -567,12 +584,21 @@ Widget fcsButton(BuildContext context, String text, {Function callack}) { height: 100.0, child: FlatButton( onPressed: callack, - child: Text(text, - style: TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.bold, - )), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + iconData == null + ? Container() + : Icon( + iconData, + color: Colors.white, + ), + SizedBox( + width: 15, + ), + Text(text, style: style), + ], + ), ), ), ), diff --git a/lib/fcs/common/pages/welcome_page.dart b/lib/fcs/common/pages/welcome_page.dart index 027e06b..ff8d79c 100644 --- a/lib/fcs/common/pages/welcome_page.dart +++ b/lib/fcs/common/pages/welcome_page.dart @@ -13,7 +13,7 @@ import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; import '../helpers/theme.dart'; -import 'profile_page.dart'; +import 'profile/profile_page.dart'; import 'signin/signin_page.dart'; final msgLog = Logger('backgroundMessageHandler'); diff --git a/lib/fcs/common/pages/widgets/display_text.dart b/lib/fcs/common/pages/widgets/display_text.dart new file mode 100644 index 0000000..fc66dd8 --- /dev/null +++ b/lib/fcs/common/pages/widgets/display_text.dart @@ -0,0 +1,69 @@ +import 'package:fcs/fcs/common/helpers/theme.dart'; +import 'package:fcs/fcs/common/pages/model/language_model.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class DisplayText extends StatelessWidget { + final String text; + final String labelText; + final IconData iconData; + final int maxLines; + final bool withBorder; + final Color borderColor; + + const DisplayText({ + Key key, + this.text, + this.labelText, + this.iconData, + this.maxLines = 1, + this.withBorder = false, + this.borderColor, + }) : super(key: key); + @override + Widget build(BuildContext context) { + var languageModel = Provider.of(context); + + var labelStyle = languageModel.isEng + ? TextStyle( + color: Colors.black54, + ) + : TextStyle(color: Colors.black54, fontFamily: "Myanmar3"); + var textStyle = languageModel.isEng + ? TextStyle( + color: primaryColor, + ) + : TextStyle(color: primaryColor, fontFamily: "Myanmar3"); + + return Padding( + padding: const EdgeInsets.only(top: 8.0, bottom: 8), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + iconData, + color: primaryColor, + ), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + labelText, + style: labelStyle, + ), + Text( + text, + style: textStyle, + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/fcs/common/services/auth_imp.dart b/lib/fcs/common/services/auth_imp.dart index 8a078bd..bfbe7cb 100644 --- a/lib/fcs/common/services/auth_imp.dart +++ b/lib/fcs/common/services/auth_imp.dart @@ -1,5 +1,5 @@ import 'package:fcs/fcs/common/data/providers/auth_fb.dart'; -import 'package:fcs/fcs/common/data/providers/user_fb_data_provider.dart'; +import 'package:fcs/fcs/common/data/providers/user_data_provider.dart'; import 'package:fcs/fcs/common/data/providers/user_local_data_provider.dart'; import 'package:fcs/fcs/common/domain/entities/auth_result.dart'; import 'package:fcs/fcs/common/domain/entities/connectivity.dart'; @@ -13,12 +13,10 @@ class AuthServiceImp implements AuthService { AuthServiceImp({ @required this.authFb, @required this.connectivity, - @required this.userFBDataProvider, @required this.userLocalDataProvider, }); final Connectivity connectivity; - final UserFBDataProvider userFBDataProvider; final UserLocalDataProvider userLocalDataProvider; final AuthFb authFb; @@ -61,4 +59,14 @@ class AuthServiceImp implements AuthService { Future getToken() { return authFb.getToken(); } + + @override + Future hasInvite() { + return authFb.hasInvite(); + } + + @override + Future updateProfile(String newUserName) { + return authFb.updateProfile(newUserName); + } } diff --git a/lib/fcs/common/services/auth_service.dart b/lib/fcs/common/services/auth_service.dart index 5344e28..216db36 100644 --- a/lib/fcs/common/services/auth_service.dart +++ b/lib/fcs/common/services/auth_service.dart @@ -8,6 +8,8 @@ abstract class AuthService { Future signout(); Future getUser({bool refreshIdToken = false}); Future signup(String userName); + Future updateProfile(String newUserName); + Future hasInvite(); Stream getSetting(); Stream onAuthStatus(); Future getToken(); diff --git a/lib/fcs/common/services/services.dart b/lib/fcs/common/services/services.dart index 560525d..059404c 100644 --- a/lib/fcs/common/services/services.dart +++ b/lib/fcs/common/services/services.dart @@ -1,7 +1,10 @@ import 'package:fcs/fcs/common/data/providers/auth_fb.dart'; +import 'package:fcs/fcs/common/data/providers/user_data_provider.dart'; import 'package:fcs/fcs/common/services/auth_imp.dart'; import 'package:fcs/fcs/common/services/messaging_imp.dart'; import 'package:fcs/fcs/common/services/messaging_service.dart'; +import 'package:fcs/fcs/common/services/user_imp.dart'; +import 'package:fcs/fcs/common/services/user_service.dart'; import 'auth_service.dart'; @@ -9,16 +12,19 @@ class Services { static final Services instance = Services._(); AuthService _authService; + UserService _userService; MessagingService _messagingService; Services._() { _authService = AuthServiceImp( authFb: AuthFb.instance, connectivity: null, - userFBDataProvider: null, userLocalDataProvider: null); + _userService = UserServiceImp( + connectivity: null, userDataProvider: UserDataProvider()); _messagingService = MessagingServiceImp(); } AuthService get authService => _authService; + UserService get userService => _userService; MessagingService get messagingService => _messagingService; } diff --git a/lib/fcs/common/services/user_imp.dart b/lib/fcs/common/services/user_imp.dart index c863333..42a7763 100644 --- a/lib/fcs/common/services/user_imp.dart +++ b/lib/fcs/common/services/user_imp.dart @@ -1,33 +1,36 @@ +import 'package:fcs/fcs/common/data/providers/user_data_provider.dart'; import 'package:fcs/fcs/common/domain/entities/connectivity.dart'; import 'package:fcs/fcs/common/domain/entities/user.dart'; -import 'package:fcs/fcs/common/domain/exceiptions/server_exceptions.dart'; import 'package:flutter/material.dart'; -import 'user_interface.dart'; +import 'user_service.dart'; -class UserImp implements UserInterface { - UserImp({ +class UserServiceImp implements UserService { + UserServiceImp({ @required this.connectivity, + @required this.userDataProvider, }); final Connectivity connectivity; + final UserDataProvider userDataProvider; @override - Future getUser(String id) async { - if (connectivity.isConnected) { - try { - final User user = User(); - // await userFBDataProvider.getUser(id); - // cache product - // productLocalDataProvider.cacheProduct(product); - return user; - } catch (e) { - print(e); - return ServerException()(); - } - } else { - return Future.value(User()); - // return userLocalDataProvider.getUser(id); - } + Future inviteUser(String userName, String phoneNumber) { + return userDataProvider.inviteUser(userName, phoneNumber); + } + + @override + Future deleteInvite(String phoneNumber) { + return userDataProvider.deleteInvite(phoneNumber); + } + + @override + Future acceptRequest(String userID) { + return userDataProvider.acceptRequest(userID); + } + + @override + Future findUser(String phoneNumber) { + return userDataProvider.findUser(phoneNumber); } } diff --git a/lib/fcs/common/services/user_interface.dart b/lib/fcs/common/services/user_interface.dart deleted file mode 100644 index 31d48f4..0000000 --- a/lib/fcs/common/services/user_interface.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:fcs/fcs/common/domain/entities/user.dart'; - -abstract class UserInterface { - Future getUser(String id); -} diff --git a/lib/fcs/common/services/user_service.dart b/lib/fcs/common/services/user_service.dart new file mode 100644 index 0000000..69abe7a --- /dev/null +++ b/lib/fcs/common/services/user_service.dart @@ -0,0 +1,8 @@ +import 'package:fcs/fcs/common/domain/entities/user.dart'; + +abstract class UserService { + Future inviteUser(String userName, String phoneNumber); + Future deleteInvite(String phoneNumber); + Future acceptRequest(String userID); + Future findUser(String phoneNumber); +} diff --git a/pubspec.lock b/pubspec.lock index 8c81efa..753e084 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -245,7 +245,21 @@ packages: name: firebase_auth url: "https://pub.dartlang.org" source: hosted - version: "0.14.0+9" + version: "0.16.1" + firebase_auth_platform_interface: + dependency: transitive + description: + name: firebase_auth_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.8" + firebase_auth_web: + dependency: transitive + description: + name: firebase_auth_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3+1" firebase_core: dependency: "direct main" description: @@ -328,6 +342,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.7.5" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.4+4" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" flutter_localizations: dependency: "direct main" description: flutter @@ -709,6 +737,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0" + share: + dependency: "direct main" + description: + name: share + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.5" shared_preferences: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d4610db..316c27a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -75,6 +75,7 @@ dependencies: barcode_scan: ^3.0.1 flutter_pdfview: ^1.0.3 flutter_local_notifications: ^1.4.4+4 + share: ^0.6.5