From 96a095e2e97730150d0ed434a42392280815a36a Mon Sep 17 00:00:00 2001 From: sainw Date: Fri, 11 Sep 2020 16:14:36 +0630 Subject: [PATCH] add faq --- assets/fonts/Myanmar3_2018.ttf | Bin 0 -> 107792 bytes assets/local/localization_en.json | 2 + assets/local/localization_mu.json | 3 +- lib/app.dart | 6 +- lib/fcs/common/data/providers/auth_fb.dart | 7 + lib/fcs/common/domain/entities/customer.dart | 36 ++ lib/fcs/common/domain/entities/faq.dart | 33 +- lib/fcs/common/domain/entities/role.dart | 72 ++++ lib/fcs/common/domain/entities/user.dart | 263 ++----------- lib/fcs/common/helpers/theme.dart | 9 +- .../common/pages/contact/contact_editor.dart | 118 +++--- .../common/pages/contact/contact_page.dart | 167 ++++---- .../pages/customers/customer_editor.dart | 363 ++++++++++++++++++ .../common/pages/customers/customer_list.dart | 142 +++++++ .../pages/customers/invitation_page.dart | 67 ++++ .../pages/customers/model/customer_model.dart | 66 ++++ lib/fcs/common/pages/faq/faq_detail_page.dart | 142 ++++--- lib/fcs/common/pages/faq/faq_edit_page.dart | 171 +++++---- lib/fcs/common/pages/faq/faq_page.dart | 183 +++------ lib/fcs/common/pages/faq/model/faq_model.dart | 40 +- lib/fcs/common/pages/home_page.dart | 32 +- lib/fcs/common/pages/model/main_model.dart | 65 ++-- lib/fcs/common/pages/profile_page.dart | 99 +---- lib/fcs/common/pages/signin/code_page.dart | 8 +- lib/fcs/common/pages/signin/signin_page.dart | 9 + lib/fcs/common/pages/term/term_page.dart | 65 ++-- .../pages/widgets/bottom_up_page_route.dart | 23 ++ lib/fcs/common/pages/widgets/input_text.dart | 11 +- pubspec.yaml | 5 + 29 files changed, 1370 insertions(+), 837 deletions(-) create mode 100644 assets/fonts/Myanmar3_2018.ttf create mode 100644 lib/fcs/common/domain/entities/customer.dart create mode 100644 lib/fcs/common/domain/entities/role.dart create mode 100644 lib/fcs/common/pages/customers/customer_editor.dart create mode 100644 lib/fcs/common/pages/customers/customer_list.dart create mode 100644 lib/fcs/common/pages/customers/invitation_page.dart create mode 100644 lib/fcs/common/pages/customers/model/customer_model.dart create mode 100644 lib/fcs/common/pages/widgets/bottom_up_page_route.dart diff --git a/assets/fonts/Myanmar3_2018.ttf b/assets/fonts/Myanmar3_2018.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2a6d3c8e975f63d7ea9cfe6812e473937eff3f9c GIT binary patch literal 107792 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 m>JC?i?r_!Ta8?(vjT { final ContactModel contactModel =new ContactModel(); final TermModel termModel=new TermModel(); final MainModel mainModel = new MainModel(); + final FAQModel faqModel = new FAQModel(); final UserModel userModel = new UserModel(); final ProductModel productModel = new ProductModel(); @@ -130,7 +132,6 @@ class _AppState extends State { ..addModel(messageModel) ..addModel(shipmentRateModel) ..addModel(invoiceModel) - ..addModel(customerModel) ..addModel(discountModel); this.mainModel.init(); @@ -249,6 +250,7 @@ class _AppState extends State { ChangeNotifierProvider.value(value: mainModel2), ChangeNotifierProvider.value(value: contactModel), ChangeNotifierProvider.value(value: termModel), + ChangeNotifierProvider.value(value: faqModel), ], 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 281ad5a..8f1eb87 100644 --- a/lib/fcs/common/data/providers/auth_fb.dart +++ b/lib/fcs/common/data/providers/auth_fb.dart @@ -109,6 +109,13 @@ class AuthFb { user.hasSignup = idToken.claims.containsKey("signup") && idToken.claims["signup"]; user.phoneNumber = firebaseUser.phoneNumber; + + // add privileges + String privileges = idToken.claims["privileges"]; + if (privileges != null && privileges != "") { + user.privileges = privileges.split(":").toList(); + } + return user; } diff --git a/lib/fcs/common/domain/entities/customer.dart b/lib/fcs/common/domain/entities/customer.dart new file mode 100644 index 0000000..0fb51e7 --- /dev/null +++ b/lib/fcs/common/domain/entities/customer.dart @@ -0,0 +1,36 @@ + + +class Customer { + String id; + String name; + String phoneNumber; + String status; + + Customer({ + this.id, + this.name, + this.status, + this.phoneNumber, + }); + + factory Customer.fromMap(Map map, String docID) { + return Customer( + id: docID, + name: map['user_name'], + phoneNumber: map['phone_number'], + status: map['status'], + ); + } + + Map toMap() { + return { + 'user_name': name, + 'phone_number': phoneNumber, + }; + } + + @override + String toString() { + return 'Customer{name: $name, phoneNumber: $phoneNumber,statis:$status}'; + } +} diff --git a/lib/fcs/common/domain/entities/faq.dart b/lib/fcs/common/domain/entities/faq.dart index 4c694d7..8782c55 100644 --- a/lib/fcs/common/domain/entities/faq.dart +++ b/lib/fcs/common/domain/entities/faq.dart @@ -1,27 +1,48 @@ class FAQ { String id; + int sn; String questionEng; String questionMm; String answerEng; String answerMm; - String question(bool isEng) => isEng?questionEng:questionMm; - String answer(bool isEng) => isEng?answerEng:answerMm; - - Map images; - FAQ({this.id,this.questionEng,this.questionMm,this.answerEng,this.answerMm,this.images}); + String question(bool isEng) => isEng ? questionEng : questionMm; + String answer(bool isEng) => isEng ? answerEng : answerMm; + Map imageUrls; + FAQ( + {this.id, + this.sn, + this.questionEng, + this.questionMm, + this.answerEng, + this.answerMm, + this.imageUrls}); Map toMap() { return { + 'id': id, + 'sn':sn, 'question_eng': questionEng, 'answer_eng': answerEng, 'question_mm': questionMm, 'answer_mm': answerMm, - 'images': images, + 'images': imageUrls, }; } + factory FAQ.fromMap(Map map, String id) { + return FAQ( + id: id, + sn: map['sn'], + questionEng: map['question_eng'], + answerEng: map['answer_eng'], + questionMm: map['question_mm'], + answerMm: map['answer_mm'], + imageUrls: Map.from(map['images'] ?? Map()), + ); + } + @override String toString() { return 'FAQ{id: $id, questionEng: $questionEng,questionMm:$questionMm}'; diff --git a/lib/fcs/common/domain/entities/role.dart b/lib/fcs/common/domain/entities/role.dart new file mode 100644 index 0000000..6a2b0df --- /dev/null +++ b/lib/fcs/common/domain/entities/role.dart @@ -0,0 +1,72 @@ +class Role { + String roleID; + String roleName; + String privileges; + Role({this.roleName, this.roleID, this.privileges}); + + Role.fromJson(Map json) { + roleName = json['role_name']; + roleID = json['role_id']; + privileges = json['privileges']; + } + +} + +class Parser { + String status; + String message; + Role data; + Parser({this.status, this.message, this.data}); + + Parser.fromJson(Map json) { + status = json['status']; + message = json['message']; + if (json['status'] == 'Ok') { + data = Role.fromJson(json['data']); + } + } +} + +class StatusParser { + String status; + String message; + StatusParser(this.status, this.message); + + StatusParser.fromJson(Map json) { + status = json['status']; + message = json['message']; + } +} + +class Privilege { + String id; + String name; + String desc; + bool sysAdminOnly = true; + bool isChecked = false; + + Privilege({this.id, this.name, this.desc, this.isChecked, this.sysAdminOnly}); + + factory Privilege.fromMap(Map map, String docID) { + return Privilege( + id: docID, + name: map['name'], + desc: map['desc'], + sysAdminOnly: map['sys_admin_only']); + } +} + +class UserLevel { + String id; + String name; + int level; + UserLevel({this.id, this.name, this.level}); + + factory UserLevel.fromMap(Map map, String docID) { + return UserLevel( + id: docID, + name: map['name'], + level: map['level'] + ); + } +} diff --git a/lib/fcs/common/domain/entities/user.dart b/lib/fcs/common/domain/entities/user.dart index 768d8b1..56309fe 100644 --- a/lib/fcs/common/domain/entities/user.dart +++ b/lib/fcs/common/domain/entities/user.dart @@ -1,283 +1,70 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; - class User { String id; String name; String phoneNumber; bool hasSignup; + List privileges = []; String fcsID; - String shippingAddress; - String deliveryAddress; + String get phone => phoneNumber != null && phoneNumber.startsWith("959") ? "0${phoneNumber.substring(2)}" : phoneNumber; - List claimPrivileges = []; - - final String dateofBirth; - final String gender; - final String status; - final bool disable; - bool registeredBuyer; - List privilegeIds; - String roleName; - String roleID; - bool agreeTerms; - String bizID; - String accountID; - String email; - bool isBlock; - int userLevel; - String userLevelID; - - String frontUrl; - String backUrl; - String selfieUrl; - - DateTime lastActiveTime; - String device; - - String primaryDeviceID; - String primaryDeviceName; - - String pin; - - String get getname => this.name; - String get getphonenumber => this.phoneNumber; - String get getdateofBirth => this.dateofBirth; - bool get getdisable => this.disable; - - Future setFirebaseUser(FirebaseUser firebaseUser) async { - IdTokenResult idToken = await firebaseUser.getIdToken(refresh: true); - String privileges = idToken.claims["privileges"]; - if (privileges == null || privileges == "") return; - this.claimPrivileges = privileges.split(":").toList(); - - this.accountID = idToken.claims["account_id"]; - this.bizID = idToken.claims["biz_id"]; - } - - User( - {this.id, - this.name, - this.gender, - this.phoneNumber, - this.fcsID, - this.shippingAddress, - this.deliveryAddress, - this.dateofBirth, - this.roleName, - this.roleID, - this.privilegeIds, - this.email, - this.disable, - this.status, - this.frontUrl, - this.backUrl, - this.selfieUrl, - this.registeredBuyer, - this.agreeTerms, - this.lastActiveTime, - this.device, - this.primaryDeviceID, - this.primaryDeviceName, - this.isBlock, - this.userLevel, - this.userLevelID, - this.pin}); + User({ + this.id, + this.name, + this.phoneNumber, + this.fcsID, + }); factory User.fromJson(Map json) { return User( - id: json['id'], - name: json['user_name'], - phoneNumber: json['phone_number'], - dateofBirth: json['dob'], - gender: json['gender'], - frontUrl: json['front_url'], - backUrl: json['back_url'], - selfieUrl: json['selfie_url'], - status: json['status'], - agreeTerms: json['agree_terms'], - disable: json['disable'], - registeredBuyer: json['registered_buyer'], - privilegeIds: json['privileges'], - email: json['email'], - isBlock: json['black_list'], - userLevel: json['user_level'], - userLevelID: json['user_level_id'], - pin: json['pin']); - } - - factory User.fromUserJson(Map json) { - DateTime parsedDate = DateTime.parse(json['last_active_time']); - - return User( - id: json['id'], - name: json['user_name'], - phoneNumber: json['phone_number'], - dateofBirth: json['dob'], - roleName: json['role_name'], - roleID: json['role_id'], - disable: json['disable'], - gender: json['gender'], - status: json['status'], - lastActiveTime: parsedDate == null ? null : parsedDate, - device: json['last_active_device'], - email: json['email'], - primaryDeviceID: json['primary_device_id'], - primaryDeviceName: json['primary_device_name'], - userLevel: json['user_level'], - userLevelID: json['user_level_id'], - pin: json['pin']); + id: json['id'], + name: json['user_name'], + phoneNumber: json['phone_number'], + ); } Map toJson() => { 'id': id, 'user_name': name, - 'gender': gender, 'phone_number': phoneNumber, - 'dob': dateofBirth, - 'roleName': roleName, - 'roleId': roleID, - 'disable': disable, - 'status': status, - 'registered_buyer': registeredBuyer, - 'agree_terms': agreeTerms, - 'front_url': frontUrl, - 'back_url': backUrl, - 'selfie_url': selfieUrl, - 'email': email, - 'black_list': isBlock, - 'user_level': userLevel, - 'user_level_id': userLevelID, - 'pin': pin, - 'privileges': privilegeIds, }; Map toMap() { return { 'user_name': name, 'phone_number': phoneNumber, - 'dob': dateofBirth, - 'role_name': roleName, - 'role_id': roleID, - 'disable': disable, - 'gender': gender, - 'status': status, - 'email': email, - 'black_list': isBlock, - 'user_level': userLevel, - 'user_level_id': userLevelID, - 'pin': pin }; } factory User.fromMap(Map map, String docID) { - var activeTime = (map['last_active_time'] as Timestamp); return User( - id: docID, - name: map['user_name'], - phoneNumber: map['phone_number'], - privilegeIds: - map['privileges'] == null ? [] : map['privileges'].cast(), - dateofBirth: map['dob'], - roleName: map['role_name'], - roleID: map['role_id'], - disable: map['disable'], - gender: map['gender'], - status: map['status'], - registeredBuyer: map['registered_buyer'], - agreeTerms: map['agree_terms'] == null ? false : map['agree_terms'], - lastActiveTime: activeTime == null ? null : activeTime.toDate(), - device: map['last_active_device'], - email: map['email'], - primaryDeviceID: map['primary_device_id'], - primaryDeviceName: map['primary_device_name'], - isBlock: map['black_list'], - userLevel: map['user_level'], - userLevelID: map['user_level_id'], - pin: map['pin']); + id: docID, + name: map['user_name'], + phoneNumber: map['phone_number'], + ); } - bool isBlockUser() { - return this.isBlock == true; + bool isCustomer() { + return privileges == null || privileges.length == 0; } - bool isPrimaryDevice() { - return this.primaryDeviceID != null && this.primaryDeviceID != ''; - } - - bool isRegisteredBuyer() { - return this.registeredBuyer != null && this.registeredBuyer; - } - - bool isSysAdmin() { - return claimPrivileges != null - ? claimPrivileges.contains('sys_admin') - : false; - } - - bool isSysSupport() { - return claimPrivileges != null - ? claimPrivileges.contains('sys_support') - : false; - } - - bool isBizAdmin() { - return claimPrivileges != null ? claimPrivileges.contains('ba') : false; - } - - bool isBuyer() { - return claimPrivileges == null || claimPrivileges.length == 0; - } - - bool isEmail() { - return email != null; - } - - bool hasAccount() { - return isOwner() || - (claimPrivileges != null ? claimPrivileges.contains('a') : false); - } - - bool hasDelivery() { - return isOwner() || - (claimPrivileges != null ? claimPrivileges.contains('d') : false); - } - - bool hasBuyer() { - return isOwner() || - (claimPrivileges != null ? claimPrivileges.contains('b') : false); - } - - bool isOwner() { - return claimPrivileges != null ? claimPrivileges.contains('o') : false; - } - - bool isOwnerAndAbove() { - return isOwner() || isBizAdmin() || isSysAdmin(); + bool hasSysAdmin() { + return privileges != null ? privileges.contains('sa') : false; } bool hasAdmin() { - return isOwner() || - (claimPrivileges != null ? claimPrivileges.contains('admin') : false); + return privileges != null ? privileges.contains('admin') : false; } - bool hasDO() { - return isOwner() || - (claimPrivileges != null ? claimPrivileges.contains('do') : false); + bool hasMaintenance() { + return privileges != null ? privileges.contains('mt') : false; } - bool hasPO() { - return isOwner() || - (claimPrivileges != null ? claimPrivileges.contains('po') : false); - } - - bool hasInventory() { - return isOwner() || - (claimPrivileges != null ? claimPrivileges.contains('inv') : false); + bool hasCustomers() { + return privileges != null ? privileges.contains('c') : false; } @override diff --git a/lib/fcs/common/helpers/theme.dart b/lib/fcs/common/helpers/theme.dart index a14073b..c52f8ba 100644 --- a/lib/fcs/common/helpers/theme.dart +++ b/lib/fcs/common/helpers/theme.dart @@ -16,12 +16,13 @@ const TextStyle labelStyleMM = TextStyle( fontSize: 20, color: primaryColor, fontWeight: FontWeight.w500, - fontFamily: "MyanmarUnicode"); + height: 1, + fontFamily: "Myanmar3"); const TextStyle subMenuStyle = TextStyle(fontSize: 14, color: Colors.white, fontWeight: FontWeight.w500); const TextStyle subMenuStyleMM = TextStyle(fontSize: 14, color: Colors.white, fontWeight: FontWeight.w500, - fontFamily: "MyanmarUnicode"); + fontFamily: "Myanmar3"); const TextStyle welcomeLabelStyle = TextStyle(fontSize: 23, color: primaryColor, fontWeight: FontWeight.w500); @@ -53,13 +54,13 @@ TextStyle newLabelStyleMM( color: color == null ? secondaryColor : color, fontWeight: fontWeight == null ? FontWeight.w500 : fontWeight, decoration: underline ? TextDecoration.underline : TextDecoration.none, - fontFamily: "MyanmarUnicode"); + fontFamily: "Myanmar3"); } const TextStyle photoLabelStyle = TextStyle(color: Colors.black, fontSize: 13.0); const TextStyle photoLabelStyleMM = TextStyle( - color: Colors.black, fontSize: 13.0, fontFamily: "MyanmarUnicode"); + color: Colors.black, fontSize: 13.0, fontFamily: "Myanmar3"); const TextStyle textStyle = TextStyle(fontSize: 14, color: Colors.black87, fontWeight: FontWeight.w500); const TextStyle textStyleOdd = TextStyle( diff --git a/lib/fcs/common/pages/contact/contact_editor.dart b/lib/fcs/common/pages/contact/contact_editor.dart index 0404bf8..d43ac31 100644 --- a/lib/fcs/common/pages/contact/contact_editor.dart +++ b/lib/fcs/common/pages/contact/contact_editor.dart @@ -75,68 +75,64 @@ class _ContactEditorState extends State { return LocalProgress( inAsyncCall: _isLoading, - child: CupertinoPageScaffold( - child: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverAppBar( - leading: IconButton( - icon: Icon( - CupertinoIcons.back, - size: 30, - ), - onPressed: () => Navigator.of(context).pop(), - ), - backgroundColor: primaryColor, - expandedHeight: 150.0, - floating: true, - pinned: true, - flexibleSpace: FlexibleSpaceBar( - centerTitle: true, - titlePadding: EdgeInsets.symmetric(vertical: 10), - title: Text( - AppTranslations.of(context) - .text('contact.edit.title'), - style: TextStyle( - color: Colors.white, - )), - ), - actions: [ - IconButton( - onPressed: () => _submit(), - icon: Icon( - Icons.save, - color: Colors.white, - )) - ], - ), - ]; - }, - body: Material( - child: Form( - key: _formKey, - child: ListView( - shrinkWrap: true, - padding: EdgeInsets.only(left: 24.0, right: 24.0), - children: [ - itemTitle(context, "contact.callus"), - usaPhoneBox, - mmPhoneBox, - Divider(), - itemTitle(context, "contact.findus"), - usaAddreesBox, - mmAddressBox, - Divider(), - itemTitle(context, "contact.emailus"), - emailBox, - Divider(), - itemTitle(context, "contact.visitus"), - faceBookBox - ], - ), + child: Scaffold( + body: CustomScrollView(slivers: [ + SliverAppBar( + leading: IconButton( + icon: Icon( + CupertinoIcons.back, + size: 30, + ), + onPressed: () => Navigator.of(context).pop(), + ), + backgroundColor: primaryColor, + expandedHeight: 150.0, + floating: false, + pinned: true, + flexibleSpace: FlexibleSpaceBar( + centerTitle: true, + titlePadding: EdgeInsets.symmetric(vertical: 10), + title: + Text(AppTranslations.of(context).text('contact.edit.title'), + style: TextStyle( + color: Colors.white, + )), + ), + actions: [ + IconButton( + onPressed: () => _submit(), + icon: Icon( + Icons.save, + color: Colors.white, + )) + ], + ), + SliverList( + delegate: SliverChildListDelegate([ + Padding( + padding: const EdgeInsets.only(left:18.0,right:18), + child: Column( + children: [ + itemTitle(context, "contact.callus"), + usaPhoneBox, + mmPhoneBox, + Divider(), + itemTitle(context, "contact.findus"), + usaAddreesBox, + mmAddressBox, + Divider(), + itemTitle(context, "contact.emailus"), + emailBox, + Divider(), + itemTitle(context, "contact.visitus"), + faceBookBox + ], ), - )))); + ), + ]), + ) + ]), + )); } _submit() async { diff --git a/lib/fcs/common/pages/contact/contact_page.dart b/lib/fcs/common/pages/contact/contact_page.dart index ab131c1..8952084 100644 --- a/lib/fcs/common/pages/contact/contact_page.dart +++ b/lib/fcs/common/pages/contact/contact_page.dart @@ -22,93 +22,92 @@ class _ContactPageState extends State { @override Widget build(BuildContext context) { Setting setting = Provider.of(context).setting; + bool isEditable = context.select((MainModel m) => m.contactEditable()); + return Scaffold( - body: NestedScrollView( - headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverAppBar( - leading: IconButton( - icon: Icon( - CupertinoIcons.back, - size: 30, - ), - onPressed: () => Navigator.of(context).pop(), - ), - backgroundColor: primaryColor, - expandedHeight: 150.0, - floating: true, - pinned: true, - flexibleSpace: FlexibleSpaceBar( - centerTitle: true, - titlePadding: EdgeInsets.symmetric(vertical: 10), - title: Text(AppTranslations.of(context).text('contact.title'), - style: TextStyle( - color: Colors.white, - )), - ), - actions: [ - IconButton( - onPressed: () => - Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => ContactEditor( - contact: Contact.fromSetting(setting), - ), - )), - icon: Icon( - CupertinoIcons.pen, - color: Colors.white, - )) - ], + body: CustomScrollView(slivers: [ + SliverAppBar( + leading: IconButton( + icon: Icon( + CupertinoIcons.back, + size: 30, ), - ]; - }, - body: ListView( - children: [ - itemTitle(context, "contact.callus"), - contactItem(context, setting.usaContactNumber, CupertinoIcons.phone, - onTap: () => _call(setting.usaContactNumber), - labelKey: "contact.usa.phone"), - contactItem( - context, - setting.mmContactNumber, - CupertinoIcons.phone, - onTap: () => _call( - setting.mmContactNumber, - ), - labelKey: "contact.mm.phone", - ), - itemTitle(context, "contact.findus"), - contactItem( - context, - setting.usaAddress, - CupertinoIcons.location, - labelKey: "contact.usa.address", - ), - contactItem( - context, - setting.mmAddress, - CupertinoIcons.location, - labelKey: "contact.mm.address", - ), - itemTitle(context, "contact.emailus"), - contactItem( - context, - setting.emailAddress, - CupertinoIcons.mail, - onTap: () => _email(setting.emailAddress), - labelKey: "contact.fcs.email", - ), - itemTitle(context, "contact.visitus"), - contactItem( - context, - setting.facebookLink, - FontAwesomeIcons.facebook, - onTap: () => _opencontactItem(setting.facebookLink), - labelKey: "contact.facebook", - ), - ], + onPressed: () => Navigator.of(context).pop(), + ), + backgroundColor: primaryColor, + expandedHeight: 150.0, + floating: false, + pinned: true, + flexibleSpace: FlexibleSpaceBar( + centerTitle: true, + titlePadding: EdgeInsets.symmetric(vertical: 10), + title: Text(AppTranslations.of(context).text('contact.title'), + style: TextStyle( + color: Colors.white, + )), + ), + actions: isEditable + ? [ + IconButton( + onPressed: () => + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => ContactEditor( + contact: Contact.fromSetting(setting), + ), + )), + icon: Icon( + CupertinoIcons.pen, + color: Colors.white, + )) + ] + : [], ), - ), + SliverList( + delegate: SliverChildListDelegate([ + itemTitle(context, "contact.callus"), + contactItem(context, setting.usaContactNumber, CupertinoIcons.phone, + onTap: () => _call(setting.usaContactNumber), + labelKey: "contact.usa.phone"), + contactItem( + context, + setting.mmContactNumber, + CupertinoIcons.phone, + onTap: () => _call( + setting.mmContactNumber, + ), + labelKey: "contact.mm.phone", + ), + itemTitle(context, "contact.findus"), + contactItem( + context, + setting.usaAddress, + CupertinoIcons.location, + labelKey: "contact.usa.address", + ), + contactItem( + context, + setting.mmAddress, + CupertinoIcons.location, + labelKey: "contact.mm.address", + ), + itemTitle(context, "contact.emailus"), + contactItem( + context, + setting.emailAddress, + CupertinoIcons.mail, + onTap: () => _email(setting.emailAddress), + labelKey: "contact.fcs.email", + ), + itemTitle(context, "contact.visitus"), + contactItem( + context, + setting.facebookLink, + FontAwesomeIcons.facebook, + onTap: () => _opencontactItem(setting.facebookLink), + labelKey: "contact.facebook", + ), + ])) + ]), ); } diff --git a/lib/fcs/common/pages/customers/customer_editor.dart b/lib/fcs/common/pages/customers/customer_editor.dart new file mode 100644 index 0000000..a740afd --- /dev/null +++ b/lib/fcs/common/pages/customers/customer_editor.dart @@ -0,0 +1,363 @@ +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/pages/util.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; + 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, + ), + ), + ), + ], + ); + + 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(), + ), + backgroundColor: primaryColor, + title: LocalText( + context, + "customer.form.title", + fontSize: 20, + color: Colors.white, + ), + // 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; + }); + // 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 new file mode 100644 index 0000000..aae62c8 --- /dev/null +++ b/lib/fcs/common/pages/customers/customer_list.dart @@ -0,0 +1,142 @@ +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/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: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'; + +class CustomerList extends StatefulWidget { + @override + _CustomerListState createState() => _CustomerListState(); +} + +class _CustomerListState 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: [ + IconButton( + icon: Icon( + Icons.search, + color: Colors.white, + ), + iconSize: 30, + onPressed: () => {}, + ), + ], + backgroundColor: primaryColor, + title: LocalText( + context, + 'customer.list.title', + color: Colors.white, + 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), + ), + ], + ), + ), + ], + ); + }), + ), + ); + } +} diff --git a/lib/fcs/common/pages/customers/invitation_page.dart b/lib/fcs/common/pages/customers/invitation_page.dart new file mode 100644 index 0000000..1b93e4f --- /dev/null +++ b/lib/fcs/common/pages/customers/invitation_page.dart @@ -0,0 +1,67 @@ +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 new file mode 100644 index 0000000..57b9771 --- /dev/null +++ b/lib/fcs/common/pages/customers/model/customer_model.dart @@ -0,0 +1,66 @@ +import 'package:fcs/fcs/common/domain/entities/customer.dart'; +import 'package:fcs/fcs/common/pages/model/base_model.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'), + ]; + + + + void initUser(user) async { + super.initUser(user); + // _loadCustomer(); + } + + @override + logout() async { + customers = []; + } + + // Future _loadCustomer() async { + // if (!user.isOwnerAndAbove() && !user.hasAccount()) { + // return; + // } + + // 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 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); + // } + // } +} diff --git a/lib/fcs/common/pages/faq/faq_detail_page.dart b/lib/fcs/common/pages/faq/faq_detail_page.dart index 1a4ea58..2cf0eee 100644 --- a/lib/fcs/common/pages/faq/faq_detail_page.dart +++ b/lib/fcs/common/pages/faq/faq_detail_page.dart @@ -1,84 +1,95 @@ import 'package:fcs/fcs/common/domain/entities/faq.dart'; -import 'package:fcs/fcs/common/domain/entities/setting.dart'; -import 'package:fcs/fcs/common/domain/vo/contact.dart'; import 'package:fcs/fcs/common/helpers/theme.dart'; -import 'package:fcs/fcs/common/localization/app_translations.dart'; -import 'package:fcs/fcs/common/pages/contact/contact_editor.dart'; +import 'package:fcs/fcs/common/pages/faq/faq_edit_page.dart'; +import 'package:fcs/fcs/common/pages/faq/model/faq_model.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/util.dart'; import 'package:fcs/fcs/common/pages/widgets/local_text.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'; -class FAQDetailPage extends StatelessWidget { +class FAQDetailPage extends StatefulWidget { final FAQ faq; + const FAQDetailPage({this.faq}); + @override + _FAQDetailPageState createState() => _FAQDetailPageState(); +} - const FAQDetailPage({Key key, this.faq}) : super(key: key); +class _FAQDetailPageState extends State { + bool _isLoading = false; @override Widget build(BuildContext context) { - Setting setting = Provider.of(context).setting; - return Scaffold( - body: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverAppBar( - leading: IconButton( - icon: Icon( - CupertinoIcons.back, - size: 30, - ), - onPressed: () => Navigator.of(context).pop(), - ), - backgroundColor: primaryColor, - expandedHeight: 150.0, - floating: true, - pinned: true, - flexibleSpace: FlexibleSpaceBar( - centerTitle: true, - titlePadding: EdgeInsets.symmetric(vertical: 10), - title: Text(AppTranslations.of(context).text('faq.btn'), - style: TextStyle( - color: Colors.white, - )), - ), - actions: [ + bool isEditable = context.select((MainModel m) => m.faqEditable()); + return LocalProgress( + inAsyncCall: _isLoading, + child: Scaffold( + body: CustomScrollView(slivers: [ + SliverAppBar( + leading: IconButton( + icon: Icon( + CupertinoIcons.back, + color: primaryColor, + size: 50, + ), + onPressed: () => Navigator.of(context).pop(), + ), + backgroundColor: Colors.white, + expandedHeight: 100.0, + floating: false, + pinned: true, + flexibleSpace: FlexibleSpaceBar( + centerTitle: true, + titlePadding: EdgeInsets.symmetric(vertical: 10), + ), + actions: isEditable + ? [ IconButton( - onPressed: () => - Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => ContactEditor( - contact: Contact.fromSetting(setting), - ), - )), + onPressed: () { + showConfirmDialog(context, "faq.edit.delete.confirm", + () { + _delete(); + }); + }, icon: Icon( CupertinoIcons.delete, - color: Colors.white, + color: primaryColor, + size: 30, )), IconButton( onPressed: () => Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => ContactEditor( - contact: Contact.fromSetting(setting), - ), + builder: (context) => FAQEditor(faq: widget.faq), )), icon: Icon( CupertinoIcons.pen, - color: Colors.white, + color: primaryColor, )) - ], - ), - ]; - }, - body: Padding( - padding: const EdgeInsets.only(left:20.0,right:20), - child: ListView( - children: [getQuestion(context, faq), - SizedBox(height: 30,), - getAnwser(context, faq)], + ] + : [], + ), + SliverList( + delegate: SliverChildListDelegate([ + Padding( + padding: const EdgeInsets.all(28.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + getQuestion(context, widget.faq), + SizedBox( + height: 50, + ), + getAnwser(context, widget.faq) + ], ), - ))); + ), + ])) + ]), + ), + ); } Widget getQuestion(BuildContext context, FAQ faq) { @@ -87,19 +98,34 @@ class FAQDetailPage extends StatelessWidget { context, faq.question(isEng), fontSize: 22, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.bold, ); } - Widget getAnwser(BuildContext context, FAQ faq) { bool isEng = Provider.of(context).isEng; return TextLocalStyle( context, faq.answer(isEng), fontSize: 16, - fontWeight: FontWeight.w200, + fontWeight: FontWeight.w200, ); } + _delete() async { + setState(() { + _isLoading = true; + }); + try { + FAQModel faqModel = Provider.of(context, listen: false); + await faqModel.deleteFAQ(widget.faq); + Navigator.pop(context); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } } diff --git a/lib/fcs/common/pages/faq/faq_edit_page.dart b/lib/fcs/common/pages/faq/faq_edit_page.dart index d3bca39..6b2d442 100644 --- a/lib/fcs/common/pages/faq/faq_edit_page.dart +++ b/lib/fcs/common/pages/faq/faq_edit_page.dart @@ -1,6 +1,7 @@ import 'package:fcs/fcs/common/domain/entities/faq.dart'; import 'package:fcs/fcs/common/helpers/theme.dart'; import 'package:fcs/fcs/common/localization/app_translations.dart'; +import 'package:fcs/fcs/common/pages/faq/model/faq_model.dart'; import 'package:fcs/fcs/common/pages/faq/widgets.dart'; import 'package:fcs/fcs/common/pages/util.dart'; import 'package:fcs/fcs/common/pages/widgets/input_text.dart'; @@ -8,6 +9,7 @@ import 'package:fcs/fcs/common/pages/widgets/progress.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_icons/flutter_icons.dart'; +import 'package:provider/provider.dart'; class FAQEditor extends StatefulWidget { final FAQ faq; @@ -17,6 +19,7 @@ class FAQEditor extends StatefulWidget { } class _FAQEditorState extends State { + TextEditingController _sn = new TextEditingController(); TextEditingController _engQ = new TextEditingController(); TextEditingController _mmQ = new TextEditingController(); TextEditingController _engA = new TextEditingController(); @@ -25,12 +28,12 @@ class _FAQEditorState extends State { final _formKey = GlobalKey(); bool _isLoading = false; bool _isNew = false; - @override void initState() { super.initState(); _isNew = widget.faq == null; if (widget.faq != null) { + _sn.text = widget.faq.sn.toString(); _engQ.text = widget.faq.questionEng; _mmQ.text = widget.faq.questionMm; _engA.text = widget.faq.answerEng; @@ -40,22 +43,29 @@ class _FAQEditorState extends State { @override Widget build(BuildContext context) { - final usaPhoneBox = InputText( + final snBox = InputText( + controller: _sn, + maxLines: 1, + withBorder: true, + textInputType: TextInputType.number, + ); + + final questionEngBox = InputText( controller: _engQ, maxLines: 2, withBorder: true, ); - final mmPhoneBox = InputText( - controller: _mmQ, + final answerEngBox = InputText( + controller: _engA, maxLines: 5, withBorder: true, ); - final usaAddreesBox = InputText( - controller: _engA, + final questionMmBox = InputText( + controller: _mmQ, maxLines: 2, withBorder: true, ); - final mmAddressBox = InputText( + final answerMmBox = InputText( controller: _mmA, maxLines: 5, withBorder: true, @@ -63,76 +73,101 @@ class _FAQEditorState extends State { return LocalProgress( inAsyncCall: _isLoading, - child: CupertinoPageScaffold( - child: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverAppBar( - leading: IconButton( - icon: Icon( - CupertinoIcons.back, - size: 30, + child: Scaffold( + body: CustomScrollView(slivers: [ + SliverAppBar( + leading: IconButton( + icon: Icon( + CupertinoIcons.back, + size: 30, + ), + onPressed: () => Navigator.of(context).pop(), + ), + backgroundColor: primaryColor, + expandedHeight: 150.0, + floating: true, + pinned: true, + flexibleSpace: FlexibleSpaceBar( + centerTitle: true, + titlePadding: EdgeInsets.symmetric(vertical: 10), + title: Text( + AppTranslations.of(context) + .text(_isNew ? 'faq.add.title' : 'faq.edit.title'), + style: TextStyle( + color: Colors.white, + )), + ), + actions: [ + IconButton( + onPressed: () => _save(), + icon: Icon( + Icons.save, + color: Colors.white, + )) + ], + ), + SliverList( + delegate: SliverChildListDelegate([ + Form( + key: _formKey, + child: Padding( + padding: EdgeInsets.only(left: 24.0, right: 24.0), + child: Column( + children: [ + Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 18.0, left: 0), + child: subItemTitle(context, "faq.edit.sn"), ), - onPressed: () => Navigator.of(context).pop(), - ), - backgroundColor: primaryColor, - expandedHeight: 150.0, - floating: true, - pinned: true, - flexibleSpace: FlexibleSpaceBar( - centerTitle: true, - titlePadding: EdgeInsets.symmetric(vertical: 10), - title: Text( - AppTranslations.of(context) - .text(_isNew?'faq.add.title':'faq.edit.title'), - style: TextStyle( - color: Colors.white, - )), - ), - actions: [ - IconButton( - onPressed: () => _submit(), - icon: Icon( - Icons.save, - color: Colors.white, - )) + Expanded(child: snBox), ], ), - ]; - }, - body: Material( - child: Form( - key: _formKey, - child: ListView( - shrinkWrap: true, - padding: EdgeInsets.only(left: 24.0, right: 24.0), - children: [ - Center(child: itemTitle(context, "faq.edit.eng")), - subItemTitle(context, "faq.edit.question",iconData: SimpleLineIcons.question), - usaPhoneBox, - subItemTitle(context, "faq.edit.answer",iconData: MaterialCommunityIcons.message_reply_text), - mmPhoneBox, - Divider(), - Center(child: itemTitle(context, "faq.edit.mm")), - subItemTitle(context, "faq.edit.question",iconData: SimpleLineIcons.question), - usaAddreesBox, - subItemTitle(context, "faq.edit.answer",iconData: MaterialCommunityIcons.message_reply_text), - mmAddressBox, - ], - ), - ), - )))); + Center(child: itemTitle(context, "faq.edit.eng")), + subItemTitle(context, "faq.edit.question", + iconData: SimpleLineIcons.question), + questionEngBox, + subItemTitle(context, "faq.edit.answer", + iconData: MaterialCommunityIcons.message_reply_text), + answerEngBox, + Divider(), + Center(child: itemTitle(context, "faq.edit.mm")), + subItemTitle(context, "faq.edit.question", + iconData: SimpleLineIcons.question), + questionMmBox, + subItemTitle(context, "faq.edit.answer", + iconData: MaterialCommunityIcons.message_reply_text), + answerMmBox, + ], + ), + ), + ), + ])) + ]))); } - _submit() async { + _save() async { setState(() { _isLoading = true; }); try { - widget.faq.questionEng = _engQ.text; - // var contactModel = Provider.of(context, listen: false); - // await contactModel.saveContact(widget.contact); + int sn = int.parse( + _sn.text, + onError: (source) => throw Exception("Invalid number"), + ); + FAQModel faqModel = Provider.of(context, listen: false); + FAQ _faq = FAQ( + sn: sn, + questionEng: _engQ.text, + answerEng: _engA.text, + questionMm: _mmQ.text, + answerMm: _mmA.text); + if (_isNew) { + await faqModel.addFAQ(_faq); + } else { + _faq.id = widget.faq.id; + await faqModel.updateFAQ(_faq); + } Navigator.pop(context); } catch (e) { showMsgDialog(context, "Error", e.toString()); diff --git a/lib/fcs/common/pages/faq/faq_page.dart b/lib/fcs/common/pages/faq/faq_page.dart index 3233f43..0ffbde2 100644 --- a/lib/fcs/common/pages/faq/faq_page.dart +++ b/lib/fcs/common/pages/faq/faq_page.dart @@ -1,22 +1,16 @@ import 'package:fcs/fcs/common/domain/entities/faq.dart'; -import 'package:fcs/fcs/common/domain/entities/setting.dart'; -import 'package:fcs/fcs/common/domain/vo/contact.dart'; import 'package:fcs/fcs/common/helpers/theme.dart'; import 'package:fcs/fcs/common/localization/app_translations.dart'; -import 'package:fcs/fcs/common/pages/contact/contact_editor.dart'; -import 'package:fcs/fcs/common/pages/contact/widgets.dart'; import 'package:fcs/fcs/common/pages/faq/faq_detail_page.dart'; import 'package:fcs/fcs/common/pages/faq/faq_edit_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/widgets/local_text.dart'; -import 'package:fcs/widget/local_text.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:package_info/package_info.dart'; import 'package:provider/provider.dart'; -import 'package:url_launcher/url_launcher.dart'; + +import 'model/faq_model.dart'; class FAQPage extends StatefulWidget { @override @@ -24,132 +18,62 @@ class FAQPage extends StatefulWidget { } class _FAQPageState extends State { - List faqs = []; @override void initState() { super.initState(); - faqs = [ - FAQ(questionEng: "Question 1?", answerEng: "Answer 1."), - FAQ(questionEng: "Question 2?", answerEng: "Answer 2."), - FAQ(questionEng: "Question 3?", answerEng: "Answer 3."), - FAQ(questionEng: "Question 4?", answerEng: "Answer 4."), - FAQ(questionEng: "Question 5?", answerEng: "Answer 5.") - ]; } @override Widget build(BuildContext context) { + FAQModel faqModel = Provider.of(context); + bool isEditable = context.select((MainModel m) => m.faqEditable()); + return Scaffold( body: CustomScrollView( slivers: [ SliverAppBar( - leading: IconButton( - icon: Icon( - CupertinoIcons.back, - size: 30, - ), - onPressed: () => Navigator.of(context).pop(), - ), - backgroundColor: primaryColor, - expandedHeight: 150.0, - floating: false, - pinned: true, - flexibleSpace: FlexibleSpaceBar( - centerTitle: true, - titlePadding: EdgeInsets.symmetric(vertical: 10), - title: Text(AppTranslations.of(context).text('faq.title'), - style: TextStyle( - color: Colors.white, - )), - ), - actions: [ - IconButton( - onPressed: () => - Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => FAQEditor(), - )), - icon: Icon( - CupertinoIcons.add, - color: Colors.white, - size: 35, - )) - ], - ), + leading: IconButton( + icon: Icon( + CupertinoIcons.back, + size: 30, + ), + onPressed: () => Navigator.of(context).pop(), + ), + backgroundColor: primaryColor, + expandedHeight: 150.0, + floating: false, + pinned: true, + flexibleSpace: FlexibleSpaceBar( + centerTitle: true, + titlePadding: EdgeInsets.symmetric(vertical: 10), + title: Text(AppTranslations.of(context).text('faq.title'), + style: TextStyle( + color: Colors.white, + )), + ), + actions:isEditable? [ + IconButton( + onPressed: () => + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => FAQEditor(), + )), + icon: Icon( + CupertinoIcons.add, + color: Colors.white, + size: 35, + )) + ]:[], + ), SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) => _faqItem(context, faqs[index]), - childCount: faqs.length, - ), -) - - // SliverFillRemaining( - // child: Padding( - // padding: const EdgeInsets.only(left: 18.0, right: 18), - // child: ListView.separated ( - // separatorBuilder: (_, i) => Divider(), - // itemCount: faqs.length, - // itemBuilder: (BuildContext ctxt, int index) { - // return _faqItem(context, faqs[index]); - // }), - // )), + delegate: SliverChildBuilderDelegate( + (context, index) => _faqItem(context, faqModel.faqs[index]), + childCount: faqModel.faqs.length, + ), + ) ], )); } - @override - Widget build1(BuildContext context) { - Setting setting = Provider.of(context).setting; - return Scaffold( - body: NestedScrollView( - headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverAppBar( - leading: IconButton( - icon: Icon( - CupertinoIcons.back, - size: 30, - ), - onPressed: () => Navigator.of(context).pop(), - ), - backgroundColor: primaryColor, - expandedHeight: 150.0, - floating: true, - pinned: true, - flexibleSpace: FlexibleSpaceBar( - centerTitle: true, - titlePadding: EdgeInsets.symmetric(vertical: 10), - title: Text(AppTranslations.of(context).text('faq.title'), - style: TextStyle( - color: Colors.white, - )), - ), - actions: [ - IconButton( - onPressed: () => - Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => FAQEditor(), - )), - icon: Icon( - CupertinoIcons.add, - color: Colors.white, - size: 35, - )) - ], - ), - ]; - }, - body: Padding( - padding: const EdgeInsets.only(left: 18.0, right: 18), - child: ListView.separated( - separatorBuilder: (_, i) => Divider(), - itemCount: faqs.length, - itemBuilder: (BuildContext ctxt, int index) { - return _faqItem(context, faqs[index]); - }), - )), - ); - } - Widget _faqItem(BuildContext context, FAQ faq) { bool isEng = Provider.of(context).isEng; return Column( @@ -167,16 +91,21 @@ class _FAQPageState extends State { child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - TextLocalStyle( - context, - faq.question(isEng), - fontSize: 16, + Flexible( + child: TextLocalStyle( + context, + faq.question(isEng), + fontSize: 18, + ), ), - Spacer(), - Icon( - CupertinoIcons.right_chevron, - color: primaryColor, - size: 22, + // Spacer(), + Padding( + padding: const EdgeInsets.only(left:18.0), + child: Icon( + CupertinoIcons.right_chevron, + color: primaryColor, + size: 22, + ), ) ], )), diff --git a/lib/fcs/common/pages/faq/model/faq_model.dart b/lib/fcs/common/pages/faq/model/faq_model.dart index d7082be..b7ceb08 100644 --- a/lib/fcs/common/pages/faq/model/faq_model.dart +++ b/lib/fcs/common/pages/faq/model/faq_model.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:fcs/fcs/common/domain/entities/faq.dart'; import 'package:fcs/fcs/common/pages/model/base_model.dart'; import 'package:fcs/fcs/common/services/services.dart'; @@ -8,10 +9,45 @@ import 'package:logging/logging.dart'; class FAQModel extends BaseModel { final log = Logger('FAQModel'); + List faqs = []; + + StreamSubscription listener; + + FAQModel() { + Stream query = Firestore.instance + .collection("/faqs") + .orderBy("sn", descending: false) + .snapshots(); + if (listener != null) { + listener.cancel(); + } + listener = query.listen((snaps) { + faqs.clear(); + snaps.documents.forEach((d) { + faqs.add(FAQ.fromMap(d.data, d.documentID)); + }); + notifyListeners(); + }); + } + Future addFAQ(FAQ faq) async { - await request("/faq", "POST", - payload: faq.toMap(), token: await Services.instance.authService.getToken()); + await request("/faqs", "POST", + payload: faq.toMap(), + token: await Services.instance.authService.getToken()); notifyListeners(); } + Future updateFAQ(FAQ faq) async { + await request("/faqs", "PUT", + payload: faq.toMap(), + token: await Services.instance.authService.getToken()); + notifyListeners(); + } + + Future deleteFAQ(FAQ faq) async { + await request("/faqs", "DELETE", + payload: faq.toMap(), + token: await Services.instance.authService.getToken()); + notifyListeners(); + } } diff --git a/lib/fcs/common/pages/home_page.dart b/lib/fcs/common/pages/home_page.dart index 7baee50..f222cb0 100644 --- a/lib/fcs/common/pages/home_page.dart +++ b/lib/fcs/common/pages/home_page.dart @@ -1,4 +1,5 @@ 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/model/language_model.dart'; import 'package:fcs/fcs/common/pages/model/main_model.dart'; @@ -25,7 +26,6 @@ import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; import '../../../pages/buying_online.dart'; -import '../../../pages/customer_list.dart'; import '../../../pages/fcs_profile_page.dart'; import '../../../pages/invoice/invoce_list.dart'; import '../../../pages/pickup_list.dart'; @@ -71,7 +71,7 @@ class _HomePageState extends State { @override Widget build(BuildContext context) { login = Provider.of(context).isLogin(); - var owner = Provider.of(context).isOwner(); + // var owner =true;// Provider.of(context).isOwner(); var customer = Provider.of(context).isCustomer(); LanguageModel languageModel = Provider.of(context); @@ -169,20 +169,20 @@ class _HomePageState extends State { List widgets = []; widgets.add(faqBtn); - customer ? widgets.add(buyingBtn) : ""; - customer || owner ? widgets.add(pickUpBtn) : ""; - owner ? 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) : ""; - owner ? widgets.add(customersBtn) : ""; - customer || owner ? widgets.add(invoicesBtn) : ""; - owner ? widgets.add(paymentMethodBtn) : ""; - owner ? widgets.add(discountBtn) : ""; + // 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); return OfflineRedirect( diff --git a/lib/fcs/common/pages/model/main_model.dart b/lib/fcs/common/pages/model/main_model.dart index c408e95..de1a321 100644 --- a/lib/fcs/common/pages/model/main_model.dart +++ b/lib/fcs/common/pages/model/main_model.dart @@ -9,6 +9,7 @@ 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/helpers/network_connectivity.dart'; +import 'package:fcs/fcs/common/pages/model/base_model.dart'; import 'package:fcs/fcs/common/services/services.dart'; import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; @@ -16,6 +17,7 @@ import 'package:package_info/package_info.dart'; class MainModel extends ChangeNotifier { final log = Logger('MainModel'); + List models = []; User user; PackageInfo packageInfo; @@ -37,67 +39,50 @@ class MainModel extends ChangeNotifier { notifyListeners(); }); Services.instance.authService.onAuthStatus().listen((event) { + this.user=event; + notifyListeners(); print("main event-->$event"); }); } + bool faqEditable(){ + return this.user != null && false; + } + bool termEditable(){ + return this.user != null && false; + } + bool contactEditable(){ + return this.user != null && false; + } + + + bool isLogin() { return this.user != null; } bool isCustomer() { - return user != null && user.name != "Owner"; - } - - bool isOwner() { - return user != null && user.name == "Owner"; - } - - bool hasEmail() { - return this.user != null && this.user.isEmail(); - } - - bool agreedTerm() { - return this.user != null && this.user.agreeTerms; - } - - bool isBuyer() { - return this.user == null || this.user.isBuyer(); + return user != null && user.isCustomer(); } bool isSysAdmin() { - return this.user != null && this.user.isSysAdmin(); + return this.user != null && this.user.hasSysAdmin(); } - bool isSysSupport() { - return this.user != null && this.user.isSysSupport(); - } - - bool isBizAdmin() { - return this.user != null && this.user.isBizAdmin(); - } - - bool isOwnerAndAbove() { - return this.user != null && this.user.isOwnerAndAbove(); - } bool isAdmin() { return this.user != null && this.user.hasAdmin(); } - bool showHistoryBtn() { - return isSysAdmin() || isSysSupport() || isBizAdmin(); - } - init() async { await _loadSetting(); _loadUser(); this.packageInfo = await PackageInfo.fromPlatform(); } - // void addModel(BaseModel model) { - // models.add(model); - // } + void addModel(BaseModel model) { + models.add(model); + } // void _initUser(User user) { // models.forEach((m) => m.initUser(user)); @@ -107,18 +92,18 @@ class MainModel extends ChangeNotifier { // } // } - // void _initSetting(Setting setting) { - // models.forEach((m) => m.initSetting(setting)); - // } + void _initSetting(Setting setting) { + models.forEach((m) => m.initSetting(setting)); + } Future _loadSetting() async { try { Services.instance.authService.getSetting().listen((event) { this.setting = event; + _initSetting(setting); notifyListeners(); }); } finally {} - // _initSetting(setting); } void _loadUser() async { diff --git a/lib/fcs/common/pages/profile_page.dart b/lib/fcs/common/pages/profile_page.dart index e981e73..b6f6ad4 100644 --- a/lib/fcs/common/pages/profile_page.dart +++ b/lib/fcs/common/pages/profile_page.dart @@ -51,16 +51,7 @@ class _ProfileState extends State { var languageModel = Provider.of(context); MainModel mainModel = Provider.of(context); - // buildLanguage(languageModel); - _selectedDropdown(String selected) { - setState(() { - selectedLanguage = selected; - languageModel.saveLanguage(selectedLanguage); - }); - } - final namebox = Container( - // padding: EdgeInsets.only(left: 25.0, right: 25.0), padding: EdgeInsets.only(top: 10, left: 25.0, right: 25.0), child: Container( height: 45.0, @@ -137,80 +128,7 @@ class _ProfileState extends State { ], ), ); - final emailBox = Container( - padding: EdgeInsets.only(top: 10, left: 25.0, right: 25.0), - child: Row( - children: [ - Text( - AppTranslations.of(context).text("profile.email"), - 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: 35, - ), - Text( - mainModel.user == null - ? "" - : mainModel.user.email == null || mainModel.user.email == '' - ? '' - : mainModel.user.email, - style: TextStyle(fontSize: 16.0, fontStyle: FontStyle.normal), - ), - ], - ), - ); - final languageBox = Container( - padding: EdgeInsets.only(bottom: 0, top: 7, left: 25.0, right: 25.0), - child: Container( - height: 45.0, - child: Row( - children: [ - Text( - AppTranslations.of(context).text("profile.language"), - 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"), - ), - Container( - width: 140, - padding: EdgeInsets.only(left: 30), - child: Theme( - data: new ThemeData( - canvasColor: Colors.white, - ), - child: DropdownButton( - hint: Text("English"), - value: selectedLanguage, - isExpanded: true, - iconSize: 40, - items: languagesList - .map>((String value) { - return DropdownMenuItem( - value: value, - child: Text(value), - ); - }).toList(), - onChanged: _selectedDropdown), - )), - ], - ), - )); + final logoutbutton = Container( padding: EdgeInsets.only(left: 20.0, right: 24.0), @@ -283,20 +201,15 @@ class _ProfileState extends State { namebox, Padding( padding: const EdgeInsets.only(left: 18.0), - child: Icon(Icons.edit), + child: Icon(Icons.edit, color: primaryColor), ) ], ), - mainModel.isBuyer() ? Container() : getPrivilegeBox(context), - phonenumberbox, - mainModel.user == null + mainModel.isCustomer() ? Container() - : mainModel.user.email == null || - mainModel.user.email == '' - ? Container() - : emailBox, - languageBox, - getShippingAddressList(context), + : getPrivilegeBox(context), + phonenumberbox, + // getShippingAddressList(context), ], ), ), diff --git a/lib/fcs/common/pages/signin/code_page.dart b/lib/fcs/common/pages/signin/code_page.dart index f534eea..0797353 100644 --- a/lib/fcs/common/pages/signin/code_page.dart +++ b/lib/fcs/common/pages/signin/code_page.dart @@ -193,6 +193,9 @@ class _CodePageState extends State { _resend() async {} _verify() async { + setState(() { + _isLoading = true; + }); try { AuthResult auth = await context.read().signin(this.pin); if (auth.authStatus == AuthStatus.AUTH_VERIFIED) { @@ -202,12 +205,13 @@ class _CodePageState extends State { } else { Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false); } - // Provider.of(context, listen: false) - // .saveUser(pin, widget.phoneNumber); } } catch (e) { showMsgDialog(context, "Error", e.toString()); } + setState(() { + _isLoading = false; + }); } _completeResend() { diff --git a/lib/fcs/common/pages/signin/signin_page.dart b/lib/fcs/common/pages/signin/signin_page.dart index ba1d2b0..12ebcee 100644 --- a/lib/fcs/common/pages/signin/signin_page.dart +++ b/lib/fcs/common/pages/signin/signin_page.dart @@ -1,7 +1,9 @@ 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/user.dart'; import 'package:fcs/fcs/common/pages/model/main_model.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'; import 'package:flutter/material.dart'; @@ -176,6 +178,13 @@ class _SigninPageState extends State { await Navigator.of(context) .push(BottomUpPageRoute(CodePage(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); + } } if (auth.authStatus == AuthStatus.ERROR) { showMsgDialog(context, "Error", auth.authErrorMsg); diff --git a/lib/fcs/common/pages/term/term_page.dart b/lib/fcs/common/pages/term/term_page.dart index 9bb8e7f..70777df 100644 --- a/lib/fcs/common/pages/term/term_page.dart +++ b/lib/fcs/common/pages/term/term_page.dart @@ -49,8 +49,10 @@ class _TermPageState extends State { @override Widget build(BuildContext context) { Setting setting = Provider.of(context).setting; - return CupertinoPageScaffold( - child: CustomScrollView(slivers: [ + bool isEditable = context.select((MainModel m) => m.termEditable()); + + return Scaffold( + body: CustomScrollView(slivers: [ SliverAppBar( leading: IconButton( icon: Icon( @@ -60,8 +62,8 @@ class _TermPageState extends State { onPressed: () => Navigator.of(context).pop(), ), backgroundColor: primaryColor, - expandedHeight: 150.0, - floating: true, + expandedHeight: 100.0, + floating: false, pinned: true, flexibleSpace: FlexibleSpaceBar( centerTitle: true, @@ -72,34 +74,35 @@ class _TermPageState extends State { color: Colors.white, ), ), - actions: [ - IconButton( - onPressed: () => - Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => - TermEdit(term: Term.fromSetting(setting)), - )), - icon: Icon( - CupertinoIcons.pen, - color: Colors.white, - )) - ], + actions: isEditable + ? [ + IconButton( + onPressed: () => + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => + TermEdit(term: Term.fromSetting(setting)), + )), + icon: Icon( + CupertinoIcons.pen, + color: Colors.white, + )) + ] + : [], ), - SliverFillRemaining(child: ZefyrTheme( - data: ZefyrThemeData().copyWith( - defaultLineTheme: LineTheme( - textStyle: TextStyle(color: Colors.black), - padding: EdgeInsets.all(0))), - child: ZefyrScaffold( - child: ZefyrEditor( - mode: ZefyrMode.view, - padding: EdgeInsets.all(16), - controller: ZefyrController(_loadDocument(setting)), - focusNode: _focusNode, - ), - )) - - ), + SliverFillRemaining( + child: ZefyrTheme( + data: ZefyrThemeData().copyWith( + defaultLineTheme: LineTheme( + textStyle: TextStyle(color: Colors.black), + padding: EdgeInsets.all(0))), + child: ZefyrScaffold( + child: ZefyrEditor( + mode: ZefyrMode.view, + padding: EdgeInsets.all(16), + controller: ZefyrController(_loadDocument(setting)), + focusNode: _focusNode, + ), + ))), ])); } diff --git a/lib/fcs/common/pages/widgets/bottom_up_page_route.dart b/lib/fcs/common/pages/widgets/bottom_up_page_route.dart new file mode 100644 index 0000000..cd27cd9 --- /dev/null +++ b/lib/fcs/common/pages/widgets/bottom_up_page_route.dart @@ -0,0 +1,23 @@ +import 'package:flutter/cupertino.dart'; + +class BottomUpPageRoute extends PageRouteBuilder { + final Widget child; + + BottomUpPageRoute(this.child) + : super( + pageBuilder: (context, animation, secondaryAnimation) => child, + transitionsBuilder: (context, animation, secondaryAnimation, child) { + var begin = Offset(0.0, 1.0); + var end = Offset.zero; + var curve = Curves.ease; + + var tween = + Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); + + return SlideTransition( + position: animation.drive(tween), + child: child, + ); + }, + ); +} diff --git a/lib/fcs/common/pages/widgets/input_text.dart b/lib/fcs/common/pages/widgets/input_text.dart index 9cb4f8c..53b00af 100644 --- a/lib/fcs/common/pages/widgets/input_text.dart +++ b/lib/fcs/common/pages/widgets/input_text.dart @@ -13,6 +13,7 @@ class InputText extends StatelessWidget { final int maxLines; final bool withBorder; final Color borderColor; + final TextInputType textInputType; const InputText( {Key key, @@ -22,7 +23,8 @@ class InputText extends StatelessWidget { this.validator, this.maxLines = 1, this.withBorder = false, - this.borderColor}) + this.borderColor, + this.textInputType}) : super(key: key); @override Widget build(BuildContext context) { @@ -36,11 +38,12 @@ class InputText extends StatelessWidget { cursorColor: primaryColor, style: textStyle, maxLines: maxLines, + keyboardType: textInputType, decoration: new InputDecoration( - hintText: '', + // hintText: '', hintStyle: TextStyle( - height: 2.5, - ), + height: 3.5, + ), labelText: labelTextKey == null ? null : AppTranslations.of(context).text(labelTextKey), diff --git a/pubspec.yaml b/pubspec.yaml index 92cfb2c..d4610db 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -115,3 +115,8 @@ flutter: - family: MyanmarUnicode fonts: - asset: assets/fonts/MyMMUnicodeUniversal.ttf + + - family: Myanmar3 + fonts: + - asset: assets/fonts/Myanmar3_2018.ttf +