diff --git a/assets/fonts/GESS-Two-Bold.otf b/assets/fonts/GESS-Two-Bold.otf new file mode 100644 index 0000000..da99274 Binary files /dev/null and b/assets/fonts/GESS-Two-Bold.otf differ diff --git a/assets/fonts/GESS-Two-Light.otf b/assets/fonts/GESS-Two-Light.otf new file mode 100644 index 0000000..60ea874 Binary files /dev/null and b/assets/fonts/GESS-Two-Light.otf differ diff --git a/assets/fonts/GESS-Two-Medium.otf b/assets/fonts/GESS-Two-Medium.otf new file mode 100644 index 0000000..ff4a1bf Binary files /dev/null and b/assets/fonts/GESS-Two-Medium.otf differ diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 22b8589..c0594da 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -76,7 +76,7 @@ "userRoleOrTitle": "عنوان المستخدم", "codeSentToEmail": "تم ارسال الرمز للايميل", "number": "موبايل", - "english": "English", + "english": "عربي", "title": "مرحبًا", "msg": "Hello {} in the {} world ", "msg_named": "{} are written in the {lang} language", @@ -491,7 +491,7 @@ "submitAd": "إرسال الإعلان", "selectAdType": "اختر نوع الإعلان", "validUntilSubscriptionExpiration": "صالح حتى انتهاء الاشتراك", - "adsRemaining": "الإعلانات المتبقية", + "adsRemainingVar": "الإعلانات المتبقية", "youLeftAdsGivenSubscription": "لقد بقي لديك 05 من 50 إعلانًا في الاشتراك.", "updateSubscription": "تحديث الاشتراك", "workInProgress": "العمل جارٍ", @@ -561,11 +561,22 @@ "inviteFriends": "دعوة الأصدقاء", "more": "المزيد", "language": "اللغة", - "mySubscriptions": "اشتراكاتي", + "mySubscription": "اشتراكاتي", "subscriptions": "الاشتراكات", "defineLicenses": "تحديد التراخيص", "logOut": "تسجيل الخروج", "customer": "العميل", + "amountVAR": "كمية", + + + + + + + + + + "accept": "قبول", "requestAlreadyInProgress": "هذا الطلب قيد التقدم بالفعل." } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 5043cc8..3409f5f 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -76,7 +76,7 @@ "userRoleOrTitle": "User role or title", "codeSentToEmail": "Code is sent to email", "number": "Number", - "english": "عربي", + "english": "English", "title": "Hello", "msg": "Hello {} in the {} world ", "msg_named": "{} are written in the {lang} language", @@ -561,12 +561,11 @@ "termPrivacy": "Term & Privacy", "inviteFriends": "Invite Friends", "more": "More", - "language": "Language", - "mySubscriptions": "My Subscriptions", "subscriptions": "Subscriptions", "defineLicenses": "Define Licenses", "logOut": "Log Out", "customer": "Customer", "accept": "Accept", + "amountVAR": "Amount", "requestAlreadyInProgress": "This request is already in progress." } \ No newline at end of file diff --git a/lib/classes/app_state.dart b/lib/classes/app_state.dart index 1379646..89e75de 100644 --- a/lib/classes/app_state.dart +++ b/lib/classes/app_state.dart @@ -1,5 +1,6 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:mc_common_app/models/general_models/post_params_model.dart'; +import 'package:mc_common_app/models/subscriptions_models/provider_subscription_model.dart'; import 'package:mc_common_app/models/user_models/user.dart'; import 'package:mc_common_app/utils/enums.dart'; @@ -44,4 +45,12 @@ class AppState { set setCurrentLocation(v) => currentLocation = v; LatLng get getCurrentLocation => currentLocation; + + List? _providerSubscription; + + List get getproviderSubscription => _providerSubscription!; + + set setproviderSubscription(List value) { + _providerSubscription = value; + } } diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index b5ea4a0..58c89a8 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -30,6 +30,7 @@ class ApiConsts { static String ChangeEmail = "${baseUrlServices}api/Account/ChangeEmail"; static String EmailVerify = "${baseUrlServices}api/Account/EmailVerify"; static String EmailVerifyOTPVerify = "${baseUrlServices}api/Account/EmailVerifyOTPVerify"; + static String LogoutUser = "${baseUrlServices}api/Account/Logout"; static String UpdateUserImage = "${baseUrlServices}api/User_UpdateProfileImage"; static String GetUserImage = "${baseUrlServices}api/ProfileImage"; @@ -291,4 +292,5 @@ class MyLocales { class MyFonts { static const poppinsFont = "packages/mc_common_app/Poppins"; + static const gessTwoFont = "packages/mc_common_app/GessTwo"; } diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index c0acc33..47285d2 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -92,7 +92,7 @@ class CodegenLoader extends AssetLoader{ "userRoleOrTitle": "عنوان المستخدم", "codeSentToEmail": "تم ارسال الرمز للايميل", "number": "موبايل", - "english": "English", + "english": "عربي", "title": "مرحبًا", "msg": "Hello {} in the {} world ", "msg_named": "{} are written in the {lang} language", @@ -507,7 +507,7 @@ class CodegenLoader extends AssetLoader{ "submitAd": "إرسال الإعلان", "selectAdType": "اختر نوع الإعلان", "validUntilSubscriptionExpiration": "صالح حتى انتهاء الاشتراك", - "adsRemaining": "الإعلانات المتبقية", + "adsRemainingVar": "الإعلانات المتبقية", "youLeftAdsGivenSubscription": "لقد بقي لديك 05 من 50 إعلانًا في الاشتراك.", "updateSubscription": "تحديث الاشتراك", "workInProgress": "العمل جارٍ", @@ -577,13 +577,14 @@ class CodegenLoader extends AssetLoader{ "inviteFriends": "دعوة الأصدقاء", "more": "المزيد", "language": "اللغة", - "mySubscriptions": "اشتراكاتي", + "mySubscription": "اشتراكاتي", "subscriptions": "الاشتراكات", "defineLicenses": "تحديد التراخيص", "logOut": "تسجيل الخروج", "customer": "العميل", "accept": "قبول", "requestAlreadyInProgress": "هذا الطلب قيد التقدم بالفعل." + "amountVAR": "كمية" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -663,7 +664,7 @@ static const Map en_US = { "userRoleOrTitle": "User role or title", "codeSentToEmail": "Code is sent to email", "number": "Number", - "english": "عربي", + "english": "English", "title": "Hello", "msg": "Hello {} in the {} world ", "msg_named": "{} are written in the {lang} language", @@ -1079,7 +1080,7 @@ static const Map en_US = { "submitAd": "Submit Ad", "selectAdType": "Select Ad Type", "validUntilSubscriptionExpiration": "Valid Until Subscription Expiration", - "adsRemaining ": "Ads Remaining", + "adsRemainingVar ": "Ads Remaining", "youLeftAdsGivenSubscription": "You have left with 05 out of 50 ads given in the subscription.", "updateSubscription": "Update Subscription", "workInProgress": "Work In Progress", @@ -1149,12 +1150,13 @@ static const Map en_US = { "inviteFriends": "Invite Friends", "more": "More", "language": "Language", - "mySubscriptions": "My Subscriptions", + "mySubscriptions": "My Subscription", "subscriptions": "Subscriptions", "defineLicenses": "Define Licenses", "logOut": "Log Out", "customer": "Customer", "accept": "Accept", + "amountVAR": "Amount" "requestAlreadyInProgress": "This request is already in progress." }; static const Map> mapLocales = {"ar_SA": ar_SA, "en_US": en_US}; diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index b02be9e..66ce054 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -470,7 +470,7 @@ abstract class LocaleKeys { static const submitAd = 'submitAd'; static const selectAdType = 'selectAdType'; static const validUntilSubscriptionExpiration = 'validUntilSubscriptionExpiration'; - static const adsRemaining = 'adsRemaining'; + static const adsRemainingVar = 'adsRemainingVar'; static const youLeftAdsGivenSubscription = 'youLeftAdsGivenSubscription'; static const updateSubscription = 'updateSubscription'; static const workInProgress = 'workInProgress'; @@ -540,12 +540,13 @@ abstract class LocaleKeys { static const inviteFriends = 'inviteFriends'; static const more = 'more'; static const language = 'language'; - static const mySubscriptions = 'mySubscriptions'; + static const mySubscription = 'mySubscription'; static const subscriptions = 'subscriptions'; static const defineLicenses = 'defineLicenses'; static const logOut = 'logOut'; static const customer = 'customer'; static const accept = 'accept'; + static const amountVAR = 'amountVAR'; static const requestAlreadyInProgress = 'requestAlreadyInProgress'; } diff --git a/lib/models/subscriptions_models/provider_subscription_model.dart b/lib/models/subscriptions_models/provider_subscription_model.dart new file mode 100644 index 0000000..9cb2ae7 --- /dev/null +++ b/lib/models/subscriptions_models/provider_subscription_model.dart @@ -0,0 +1,97 @@ +import 'dart:convert'; + +class ProviderSubscriptionModel { + int? id; + int? subscriptionAppliedId; + int? serviceProviderId; + String? subscriptionName; + String? subscriptionDescription; + DateTime? dateStart; + DateTime? dateEnd; + int? branchesRemaining; + int? subUsersRemaining; + int? subscriptionID; + int? adsRemaining; + bool? isExpired; + bool? isActive; + bool? isUpgradeNow; + bool? isUpgradeLater; + bool? isTrialSubscription; + dynamic currentSubscription; + int? totalBranches; + int? totalSubUsers; + int? totalAds; + + ProviderSubscriptionModel({ + this.id, + this.subscriptionAppliedId, + this.serviceProviderId, + this.subscriptionName, + this.subscriptionDescription, + this.dateStart, + this.dateEnd, + this.branchesRemaining, + this.subUsersRemaining, + this.adsRemaining, + this.isExpired, + this.isActive, + this.isUpgradeNow, + this.isUpgradeLater, + this.isTrialSubscription, + this.currentSubscription, + this.subscriptionID, + this.totalAds, + this.totalBranches, + this.totalSubUsers, + }); + + factory ProviderSubscriptionModel.fromRawJson(String str) => ProviderSubscriptionModel.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory ProviderSubscriptionModel.fromJson(Map json) => ProviderSubscriptionModel( + id: json["id"], + subscriptionAppliedId: json["subscriptionAppliedID"], + serviceProviderId: json["serviceProviderID"], + subscriptionID: json["subscriptionID"], + subscriptionName: json["subscriptionName"], + subscriptionDescription: json["subscriptionDescription"], + dateStart: json["dateStart"] == null ? null : DateTime.parse(json["dateStart"]), + dateEnd: json["dateEnd"] == null ? null : DateTime.parse(json["dateEnd"]), + branchesRemaining: json["branchesRemaining"], + subUsersRemaining: json["subUsersRemaining"], + adsRemaining: json["adsRemaining"], + isExpired: json["isExpired"], + isActive: json["isActive"], + isUpgradeNow: json["isUpgradeNow"], + isUpgradeLater: json["isUpgradeLater"], + isTrialSubscription: json["isTrialSubscription"], + currentSubscription: json["currentSubscription"], + totalBranches: json["totalBranches"], + totalAds: json["totalAds"], + totalSubUsers: json["totalSubUsers"], + ); + + Map toJson() => { + "id": id, + "subscriptionAppliedID": subscriptionAppliedId, + "serviceProviderID": serviceProviderId, + "subscriptionID": subscriptionID, + "subscriptionName": subscriptionName, + "subscriptionDescription": subscriptionDescription, + "dateStart": dateStart?.toIso8601String(), + "dateEnd": dateEnd?.toIso8601String(), + "branchesRemaining": branchesRemaining, + "subUsersRemaining": subUsersRemaining, + "adsRemaining": adsRemaining, + "isExpired": isExpired, + "isActive": isActive, + "isUpgradeNow": isUpgradeNow, + "isUpgradeLater": isUpgradeLater, + "isTrialSubscription": isTrialSubscription, + "currentSubscription": currentSubscription, + "totalAds": totalAds, + "totalSubUsers": totalSubUsers, + "totalBranches": totalBranches, + }; +} diff --git a/lib/models/subscriptions_models/subscription_model.dart b/lib/models/subscriptions_models/subscription_model.dart index 66d79f8..a7e979d 100644 --- a/lib/models/subscriptions_models/subscription_model.dart +++ b/lib/models/subscriptions_models/subscription_model.dart @@ -8,8 +8,7 @@ import 'package:mc_common_app/extensions/string_extensions.dart'; import '../../utils/enums.dart'; -Subscription subscriptionFromJson(String str) => - Subscription.fromJson(json.decode(str)); +Subscription subscriptionFromJson(String str) => Subscription.fromJson(json.decode(str)); String subscriptionToJson(Subscription data) => json.encode(data.toJson()); @@ -26,23 +25,17 @@ class SubscriptionModel { List? data; String? message; - factory SubscriptionModel.fromJson(Map json) => - SubscriptionModel( + factory SubscriptionModel.fromJson(Map json) => SubscriptionModel( messageStatus: json["messageStatus"], totalItemsCount: json["totalItemsCount"], - data: json["data"] == null - ? [] - : List.from( - json["data"]!.map((x) => Subscription.fromJson(x))), + data: json["data"] == null ? [] : List.from(json["data"]!.map((x) => Subscription.fromJson(x))), message: json["message"], ); Map toJson() => { "messageStatus": messageStatus, "totalItemsCount": totalItemsCount, - "data": data == null - ? [] - : List.from(data!.map((x) => x.toJson())), + "data": data == null ? [] : List.from(data!.map((x) => x.toJson())), "message": message, }; } @@ -69,6 +62,7 @@ class Subscription { this.isExpired, this.isActive, this.subscriptionTypeEnum, + this.isMyCurrentPackage, }); int? id; @@ -91,6 +85,7 @@ class Subscription { bool? isExpired; bool? isActive; SubscriptionTypeEnum? subscriptionTypeEnum; + bool? isMyCurrentPackage; factory Subscription.fromJson(Map json) => Subscription( id: json["id"], @@ -107,16 +102,13 @@ class Subscription { countryName: json["countryName"], isSubscribed: json["isSubscribed"], subscriptionAppliedId: json["subscriptionAppliedID"], - serviceProviderId: - json["serviceProviderID"], - dateStart: - json["dateStart"] == null ? null : DateTime.parse(json["dateStart"]), + serviceProviderId: json["serviceProviderID"], + dateStart: json["dateStart"] == null ? null : DateTime.parse(json["dateStart"]), dateEnd: json["dateEnd"] == null ? null : DateTime.parse(json["dateEnd"]), isExpired: json["isExpired"], isActive: json["isActive"], - subscriptionTypeEnum: json["subscriptionType"] == null - ? null - : ((json['subscriptionType']) as int).toSubscriptionTypeEnum()); + isMyCurrentPackage: false, + subscriptionTypeEnum: json["subscriptionType"] == null ? null : ((json['subscriptionType']) as int).toSubscriptionTypeEnum()); Map toJson() => { "id": id, diff --git a/lib/repositories/appointment_repo.dart b/lib/repositories/appointment_repo.dart index 9bffe24..a581c3d 100644 --- a/lib/repositories/appointment_repo.dart +++ b/lib/repositories/appointment_repo.dart @@ -191,7 +191,7 @@ class AppointmentRepoImp implements AppointmentRepo { queryParameters: map, ApiConsts.serviceProvidersAppointmentGet, ); - List appointmentList = List.generate(genericRespModel.data.length, (index) => AppointmentListModel.fromJson(genericRespModel.data[index])); + List appointmentList = await List.generate(genericRespModel.data.length, (index) => AppointmentListModel.fromJson(genericRespModel.data[index])); return appointmentList; } diff --git a/lib/repositories/subscription_repo.dart b/lib/repositories/subscription_repo.dart index 7f5f289..fd2b037 100644 --- a/lib/repositories/subscription_repo.dart +++ b/lib/repositories/subscription_repo.dart @@ -6,6 +6,7 @@ import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/general_models/m_response.dart'; import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; import 'package:mc_common_app/models/subscriptions_models/branch_user_selection_model.dart'; +import 'package:mc_common_app/models/subscriptions_models/provider_subscription_model.dart'; import 'package:mc_common_app/models/subscriptions_models/subscription_model.dart'; import 'package:mc_common_app/models/user_models/branch_user.dart'; @@ -14,22 +15,23 @@ abstract class SubscriptionRepo { Future getMySubscriptions(String? serviceProviderID); - Future getSubscriptionBySP( - String? serviceProviderID, bool isRenew); + Future> getProviderSubscription({String? serviceProviderID}); - Future calculationUpgradePrice( - String? serviceProviderID, String? newSubscription); + Future getSubscriptionBySP(String? serviceProviderID, bool isRenew); + + Future calculationUpgradePrice(String? serviceProviderID, String? newSubscription); Future payForProviderSubscription(Map map); - Future> getSPBranchUser_Get( - Map map); + Future> getSPBranchUser_Get(Map map); } class SubscriptionRepoImp extends SubscriptionRepo { + ApiClient apiClient = injector.get(); + AppState appState = injector.get(); + @override - Future getAllSubscriptions( - String? serviceProviderID) async { + Future getAllSubscriptions(String? serviceProviderID) async { String t = AppState().getUser.data!.accessToken ?? ""; Map queryParameters = {}; if (serviceProviderID != null) { @@ -38,16 +40,11 @@ class SubscriptionRepoImp extends SubscriptionRepo { }; } - return await injector.get().getJsonForObject( - (json) => SubscriptionModel.fromJson(json), - ApiConsts.getAllSubscriptions, - token: t, - queryParameters: queryParameters); + return await injector.get().getJsonForObject((json) => SubscriptionModel.fromJson(json), ApiConsts.getAllSubscriptions, token: t, queryParameters: queryParameters); } @override - Future getSubscriptionBySP( - String? serviceProviderID, bool isRenew) async { + Future getSubscriptionBySP(String? serviceProviderID, bool isRenew) async { String t = AppState().getUser.data!.accessToken ?? ""; Map queryParameters = {}; if (serviceProviderID != null) { @@ -66,8 +63,7 @@ class SubscriptionRepoImp extends SubscriptionRepo { } @override - Future calculationUpgradePrice( - String? serviceProviderID, String? newSubscription) async { + Future calculationUpgradePrice(String? serviceProviderID, String? newSubscription) async { String t = AppState().getUser.data!.accessToken ?? ""; Map queryParameters = {}; if (serviceProviderID != null) { @@ -98,17 +94,15 @@ class SubscriptionRepoImp extends SubscriptionRepo { } @override - Future> getSPBranchUser_Get( - Map map) async { + Future> getSPBranchUser_Get(Map map) async { String t = AppState().getUser.data!.accessToken ?? ""; - GenericRespModel genericRespModel = - await injector.get().getJsonForObject( - (json) => GenericRespModel.fromJson(json), - ApiConsts.getSPBranchUser_Get, - token: t, - queryParameters: map, - ); + GenericRespModel genericRespModel = await injector.get().getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.getSPBranchUser_Get, + token: t, + queryParameters: map, + ); List branchList = []; @@ -117,36 +111,23 @@ class SubscriptionRepoImp extends SubscriptionRepo { List branchUsers = []; // List.from(json["data"]!.map((x) => BranchUsersDatum.fromJson(x)) - branches = List.from(genericRespModel.data["branches"] - ["data"]! - .map((x) => BranchDetailModel.fromJson(x))); - branchUsers = List.from(genericRespModel.data["branchUsers"] - ["data"]! - .map((x) => BranchUser.fromJson(x))); + branches = List.from(genericRespModel.data["branches"]["data"]!.map((x) => BranchDetailModel.fromJson(x))); + branchUsers = List.from(genericRespModel.data["branchUsers"]["data"]!.map((x) => BranchUser.fromJson(x))); for (int i = 0; i < branches.length; i++) { List availableUsers = []; for (int j = 0; j < branchUsers.length; j++) { if (branches[i].id == branchUsers[j].serviceProviderBranchID) { - availableUsers.add(UserSelectionModel( - userId: branchUsers[j].id, - userName: - ("${branchUsers[j].firstName ?? ""} ${branchUsers[j].lastName}"), - isSelected: false)); + availableUsers.add(UserSelectionModel(userId: branchUsers[j].id, userName: ("${branchUsers[j].firstName ?? ""} ${branchUsers[j].lastName}"), isSelected: false)); } } - branchList.add(BranchSelectionModel( - branchId: branches[i].id ?? 0, - branchName: branches[i].branchName ?? "", - isSelected: false, - usersList: availableUsers)); + branchList.add(BranchSelectionModel(branchId: branches[i].id ?? 0, branchName: branches[i].branchName ?? "", isSelected: false, usersList: availableUsers)); } } return branchList; } @override - Future getMySubscriptions( - String? serviceProviderID) async { + Future getMySubscriptions(String? serviceProviderID) async { String t = AppState().getUser.data!.accessToken ?? ""; Map queryParameters = {}; if (serviceProviderID != null) { @@ -154,12 +135,46 @@ class SubscriptionRepoImp extends SubscriptionRepo { "ID": serviceProviderID, }; } - - return await injector.get().getJsonForObject( - (json) => SubscriptionModel.fromJson(json), - ApiConsts.getAllSubscriptions, - token: t, - queryParameters: queryParameters); + return await injector.get().getJsonForObject((json) => SubscriptionModel.fromJson(json), ApiConsts.getAllSubscriptions, token: t, queryParameters: queryParameters); } + + @override + Future> getProviderSubscription({String? serviceProviderID}) async { + Map queryParameters = {}; + if (serviceProviderID != null) { + queryParameters = { + "ServiceProviderID": serviceProviderID, + "IsActiveSubscriptionForProvider": "true", + }; + } + + GenericRespModel adsGenericModel = await apiClient.getJsonForObject( + token: appState.getUser.data!.accessToken, + (json) => GenericRespModel.fromJson(json), + ApiConsts.getMySubscriptions, + queryParameters: queryParameters, + ); + List providerSubList = List.generate(adsGenericModel.data.length, (index) => ProviderSubscriptionModel.fromJson(adsGenericModel.data[index])); + return providerSubList; + } + +// @override +// Future getProviderSubscription( +// String? serviceProviderID) async { +// String t = AppState().getUser.data!.accessToken ?? ""; +// Map queryParameters = {}; +// if (serviceProviderID != null) { +// queryParameters = { +// "ID": serviceProviderID, +// }; +// } +// +// +// return await injector.get().getJsonForObject( +// (json) => SubscriptionModel.fromJson(json), +// ApiConsts.getMySubscriptions, +// token: t, +// queryParameters: queryParameters); +// } } diff --git a/lib/repositories/user_repo.dart b/lib/repositories/user_repo.dart index e423e4f..f5656ed 100644 --- a/lib/repositories/user_repo.dart +++ b/lib/repositories/user_repo.dart @@ -71,6 +71,8 @@ abstract class UserRepo { Future getUserImage(String image); + Future logoutUser(); + Future updateUserToken(); } @@ -275,7 +277,12 @@ class UserRepoImp implements UserRepo { String t = AppState().getUser.data!.accessToken ?? ""; debugPrint("token $t"); - return await injector.get().postJsonForObject((json) => ImageResponse.fromJson(json), ApiConsts.UpdateUserImage, postParams, token: t,); + return await injector.get().postJsonForObject( + (json) => ImageResponse.fromJson(json), + ApiConsts.UpdateUserImage, + postParams, + token: t, + ); } @override @@ -303,4 +310,10 @@ class UserRepoImp implements UserRepo { AppState().setUser = user; return refresh.data!.accessToken ?? ""; } + + Future logoutUser() async { + String t = AppState().getUser.data!.accessToken ?? ""; + var postParams = {"token": AppState().getUser.data!.accessToken ?? ""}; + return await injector.get().postJsonForResponse(ApiConsts.LogoutUser, postParams, token: t); + } } diff --git a/lib/theme/app_theme.dart b/lib/theme/app_theme.dart index 1fa9447..72b40fb 100644 --- a/lib/theme/app_theme.dart +++ b/lib/theme/app_theme.dart @@ -5,14 +5,14 @@ import 'colors.dart'; class AppTheme { static getTheme({required isArabic}) { - return ThemeData( - fontFamily: !isArabic ? MyFonts.poppinsFont : null, + fontFamily: !isArabic ? MyFonts.poppinsFont : MyFonts.gessTwoFont, primaryColor: primaryColor, useMaterial3: false, primaryTextTheme: const TextTheme( titleLarge: TextStyle(color: Colors.white), - ), colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.orange).copyWith(background: Colors.white), + ), + colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.orange).copyWith(background: Colors.white), ); } } diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 9158fb0..c184177 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -69,6 +69,7 @@ class AppointmentsVM extends BaseVM { List branchCategories = []; bool isHomeTapped = false; + bool isShowEmptyMessage = false; List serviceAppointmentScheduleList = []; @@ -338,12 +339,31 @@ class AppointmentsVM extends BaseVM { // }).toList(); } + // Future getMyAppointments() async { + // setState(ViewState.busy); + // + // myAppointments = + // await appointmentRepo.getMyAppointmentsForCustomersByFilters(); + // myFilteredAppointments = myAppointments; + // myUpComingAppointments = myAppointments + // .where((element) => + // element.appointmentStatusEnum == AppointmentStatusEnum.booked) + // .toList(); + // applyFilterOnAppointmentsVM( + // appointmentStatusEnum: AppointmentStatusEnum.allAppointments); + // if (myUpComingAppointments.isEmpty) { + // isShowEmptyMessage = true; + // } + // setState(ViewState.idle); + // notifyListeners(); + // } + Future getMyAppointments({bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters(); // myFilteredAppointments = myAppointments; - myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.confirmed).toList(); + myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed ).toList(); setState(ViewState.idle); applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments); notifyListeners(); @@ -365,13 +385,13 @@ class AppointmentsVM extends BaseVM { } } - Future getMyAppointmentsForProvider(Map map, {bool isNeedToRebuild = false}) async { - if (isNeedToRebuild) setState(ViewState.busy); - + Future getMyAppointmentsForProvider( + Map map, + ) async { + setState(ViewState.busy); myAppointments = await appointmentRepo.getMyAppointmentsForProvider(map); myFilteredAppointments = myAppointments; - myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.booked).toList(); - + myUpComingAppointments = await myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.confirmed).toList(); applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true); setState(ViewState.idle); } @@ -417,7 +437,7 @@ class AppointmentsVM extends BaseVM { updateCheckBoxInMergeRequest(int currentIndex) { myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList![currentIndex].isSelected = - !(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false); + !(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false); int count = countSelected(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList ?? []); if (count > 1) @@ -428,10 +448,7 @@ class AppointmentsVM extends BaseVM { } int countSelected(List appointments) { - return appointments - .where((appointment) => appointment.isSelected == true) - .toList() - .length; + return appointments.where((appointment) => appointment.isSelected == true).toList().length; } updateSelectedAppointmentDate({required int dateIndex, required int scheduleIndex}) { @@ -598,14 +615,13 @@ class AppointmentsVM extends BaseVM { Column( children: List.generate( selectedService.serviceItems!.length, - (index) => - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "${selectedService.serviceItems![index].name}".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - "${selectedService.serviceItems![index].price} SAR".toText(fontSize: 12, isBold: true), - ], - ), + (index) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "${selectedService.serviceItems![index].name}".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + "${selectedService.serviceItems![index].price} SAR".toText(fontSize: 12, isBold: true), + ], + ), ), ), Row( diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index f348195..07f928c 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -350,8 +350,7 @@ class ServiceVM extends BaseVM { setState(ViewState.idle); } - Future getAllCountriesList( - BranchDetailModel? branchData, String countryCode) async { + Future getAllCountriesList(BranchDetailModel? branchData, String countryCode) async { cities = null; country = null; setState(ViewState.busy); @@ -386,8 +385,7 @@ class ServiceVM extends BaseVM { setState(ViewState.idle); } - Future getAllCities( - BranchDetailModel? branchData, String countryCode) async { + Future getAllCities(BranchDetailModel? branchData, String countryCode) async { setState(ViewState.busy); citiesDropList = []; cities = null; @@ -432,16 +430,15 @@ class ServiceVM extends BaseVM { cityId.toString(), address, latitude.toString(), longitude.toString()); } - Future updateBranch( - int id, - String branchName, - String branchDescription, - String cityId, - String address, - String latitude, - String longitude, { - bool isNeedToDelete = true, - }) async { + Future updateBranch(int id, + String branchName, + String branchDescription, + String cityId, + String address, + String latitude, + String longitude, { + bool isNeedToDelete = true, + }) async { return await branchRepo.updateBranch( id ?? 0, branchName, @@ -488,10 +485,10 @@ class ServiceVM extends BaseVM { DropValue( element.id ?? 0, ((element.categoryName!.isEmpty - ? "N/A" - : countryCode == "SA" - ? element.categoryNameN - : element.categoryName) ?? + ? "N/A" + : countryCode == "SA" + ? element.categoryNameN + : element.categoryName) ?? "N/A"), "", ), @@ -551,8 +548,7 @@ class ServiceVM extends BaseVM { List? matchedServices; bool isAllSelected = false; - Future getAllMatchedServices( - int oldBranchId, int newBranchId, int categoryId) async { + Future getAllMatchedServices(int oldBranchId, int newBranchId, int categoryId) async { matchedServices = null; final MResponse response = await branchRepo.getMatchedServices( oldBranchId, newBranchId, categoryId); diff --git a/lib/view_models/subscriptions_view_model.dart b/lib/view_models/subscriptions_view_model.dart index 49e6d5e..f0ff88f 100644 --- a/lib/view_models/subscriptions_view_model.dart +++ b/lib/view_models/subscriptions_view_model.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/models/general_models/m_response.dart'; import 'package:mc_common_app/models/subscriptions_models/branch_user_selection_model.dart'; +import 'package:mc_common_app/models/subscriptions_models/provider_subscription_model.dart'; import 'package:mc_common_app/models/subscriptions_models/subscription_model.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/view_models/base_view_model.dart'; @@ -19,6 +20,7 @@ class SubscriptionsVM extends BaseVM { late DropValue selectedMothlyTab; List monthlyTabs = []; late SubscriptionModel allSubscriptions; + List mySubscriptionsBySp = []; List tempSubscriptions = []; //My Subscriptions @@ -33,10 +35,11 @@ class SubscriptionsVM extends BaseVM { var idSet = {}; for (var d in allSubscriptions.data ?? []) { if (idSet.add(d.durationDays ?? 0)) { - monthlyTabs.add(DropValue(d.durationDays, _convertDaysToMonths(d.durationDays ?? 0), "")); + monthlyTabs.add(DropValue(d.durationDays, _convertDaysToMonths(d.durationDays ?? 0), "", isEnabled: false)); } } monthlyTabs.sort((a, b) => a.value.compareTo(b.value)); + monthlyTabs.first.isEnabled = true; selectedMothlyTab = monthlyTabs.first; filterSubscriptions(); setState(ViewState.idle); @@ -54,11 +57,13 @@ class SubscriptionsVM extends BaseVM { var idSet = {}; for (var d in allSubscriptions.data ?? []) { if (idSet.add(d.durationDays ?? 0)) { - monthlyTabs.add(DropValue(d.durationDays, _convertDaysToMonths(d.durationDays ?? 0), "")); + monthlyTabs.add(DropValue(d.durationDays, _convertDaysToMonths(d.durationDays ?? 0), "", isEnabled: false)); } } monthlyTabs.sort((a, b) => a.value.compareTo(b.value)); + monthlyTabs.first.isEnabled = true; selectedMothlyTab = monthlyTabs.first; + filterSubscriptions(); setState(ViewState.idle); } else { @@ -110,17 +115,12 @@ class SubscriptionsVM extends BaseVM { return mResponse; } - Future createSubscriptionOrder(int subscriptionId, bool isStartNow, bool isReview, String amount, {bool isDegrade = false, List? listOfBranches, List? listOfUsers}) async { + Future createSubscriptionOrder(int subscriptionId, bool isStartNow, bool isRenew, {bool isDegrade = false, List? listOfBranches, List? listOfUsers}) async { Map map = { - // "id": subscription.id.toString(), - // "payFortOrderID": 0, "providerID": AppState().getUser.data?.userInfo?.providerId.toString() ?? "", "subscriptionID": subscriptionId.toString(), "isStartNow": isStartNow.toString(), - "subscriptionAmount": amount, - "isRenew": isReview.toString() - // "listOfBranches": [], - // "listOfUsers": [] + "isRenew": isRenew.toString() }; MResponse mResponse = await subscriptionRepo.payForProviderSubscription(map); return mResponse; @@ -159,6 +159,9 @@ class SubscriptionsVM extends BaseVM { if (selectedMothlyTab.id == element.durationDays) { tempSubscriptions.add(element); } + if (element.id == AppState().getproviderSubscription.first.subscriptionID) { + element.isMyCurrentPackage = true; + } } } @@ -168,6 +171,7 @@ class SubscriptionsVM extends BaseVM { setState(ViewState.busy); // allSubscriptions = await subscriptionRepo.getAllSubscriptions(serviceProviderID); allSubscriptions = await subscriptionRepo.getMySubscriptions(serviceProviderID); + if (allSubscriptions.messageStatus == 1) { // allSubscriptions.data!.sort((a, b) => a.value.compareTo(b.value)); setState(ViewState.idle); @@ -175,4 +179,14 @@ class SubscriptionsVM extends BaseVM { setState(ViewState.error); } } + + // My Provider Subscription + getMySubscriptionsBySP(String? serviceProviderID) async { + setState(ViewState.busy); + if (mySubscriptionsBySp.isEmpty) { + mySubscriptionsBySp = await subscriptionRepo.getProviderSubscription(serviceProviderID: serviceProviderID); + AppState().setproviderSubscription = mySubscriptionsBySp; + } + setState(ViewState.idle); + } } diff --git a/lib/view_models/user_view_model.dart b/lib/view_models/user_view_model.dart index 27e45d6..6e95508 100644 --- a/lib/view_models/user_view_model.dart +++ b/lib/view_models/user_view_model.dart @@ -1,16 +1,10 @@ import 'dart:convert'; import 'dart:io'; - -import 'package:device_info_plus/device_info_plus.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; - -import 'package:http/http.dart'; -import 'package:huawei_fido/huawei_fido.dart'; -import 'package:image_picker/image_picker.dart'; +import 'package:http/src/response.dart'; import 'package:local_auth/local_auth.dart'; -import 'package:logger/logger.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; @@ -27,7 +21,6 @@ import 'package:mc_common_app/models/user_models/confirm_password.dart'; import 'package:mc_common_app/models/user_models/country.dart'; import 'package:mc_common_app/models/user_models/forget_password_otp_compare.dart'; import 'package:mc_common_app/models/user_models/forget_password_otp_request.dart'; -import 'package:mc_common_app/models/user_models/image_response.dart'; import 'package:mc_common_app/models/user_models/login_password.dart'; import 'package:mc_common_app/models/user_models/register_user.dart'; import 'package:mc_common_app/models/user_models/user.dart'; @@ -163,7 +156,8 @@ class UserVM extends BaseVM { } } - Future performCompleteProfile(BuildContext context, { + Future performCompleteProfile( + BuildContext context, { required String password, required String confirmPassword, required String firstName, @@ -350,13 +344,10 @@ class UserVM extends BaseVM { Future performBasicOtpLoginSelectionPage(BuildContext context, {required String userToken, required AppType appType, String? loginType}) async { if (loginType == "3" || loginType == "4") { - Utils.showLoading(context); + //Utils.showLoading(context); LoginPasswordRespModel user = await userRepo.loginV2OTP(userToken, loginType!); - Utils.hideLoading(context); if (user.messageStatus == 1) { - Utils.showLoading(context); Response response2 = await userRepo.loginV2OTPVerify(user.data!.userToken ?? "", "9999"); - Utils.hideLoading(context); RegisterUserRespModel verifiedUser = RegisterUserRespModel.fromJson(jsonDecode(response2.body)); if (verifiedUser.messageStatus == 1) { User user = User.fromJson(jsonDecode(response2.body)); @@ -370,7 +361,6 @@ class UserVM extends BaseVM { navigateReplaceWithName(context, AppRoutes.dashboard); } else { Utils.showToast(LocaleKeys.onlyProviderApp.tr()); - //("Sorry, Only Customer's can log in this app"); } } else if (user.data!.userInfo!.roleId == 4) { if (user.data!.userInfo!.roleId == 4) { @@ -389,9 +379,9 @@ class UserVM extends BaseVM { } } } else { - Utils.showLoading(context); + // Utils.showLoading(context); LoginPasswordRespModel user = await userRepo.loginV2OTP(userToken, "1"); - Utils.hideLoading(context); + // Utils.hideLoading(context); if (user.messageStatus == 1) { showMDialog(context, child: OtpDialog( onClick: (String code) async { @@ -522,8 +512,8 @@ class UserVM extends BaseVM { type == ClassType.NUMBER && countryCode != null ? countryCode + phoneNum : type == ClassType.NUMBER && countryCode == null - ? phoneNum - : phoneNum, + ? phoneNum + : phoneNum, password); Utils.hideLoading(context); LoginPasswordRespModel user = LoginPasswordRespModel.fromJson(jsonDecode(response.body)); @@ -531,8 +521,8 @@ class UserVM extends BaseVM { SharedPrefManager.setPhoneOrEmail(type == ClassType.NUMBER && countryCode != null ? countryCode + phoneNum : type == ClassType.NUMBER && countryCode == null - ? phoneNum - : phoneNum); + ? phoneNum + : phoneNum); SharedPrefManager.setUserPassword(password); navigateReplaceWithName(context, AppRoutes.loginMethodSelection, arguments: user.data!.userToken); } else { @@ -628,6 +618,7 @@ class UserVM extends BaseVM { String uName = await SharedPrefManager.getPhoneOrEmail(); String pass = await SharedPrefManager.getUserPassword(); if (!loginOtherAccount && uName.isNotEmpty && pass.isNotEmpty) { + getAvailBio(); if (uName.isNum()) { performBasicOtpLoginWithPasswordPage(context, type: ClassType.NUMBER, countryCode: null, phoneNum: uName, password: pass); } @@ -642,4 +633,28 @@ class UserVM extends BaseVM { _loginOtherAccount = true; navigateReplaceWithName(context, AppRoutes.loginWithPassword); } + + void changeLanguage(BuildContext context) { + if (EasyLocalization.of(context)?.currentLocale?.countryCode == "SA") { + context.setLocale(const Locale("en", "US")); + } else { + context.setLocale(const Locale('ar', 'SA')); + } + notifyListeners(); + } + + void logout(BuildContext context) async { + Response value = await userRepo.logoutUser(); + if (value.body.isNotEmpty) { + print(value.body); + print("Logout"); + } + SharedPrefManager.setPhoneOrEmail(""); + SharedPrefManager.setUserPassword(""); + if (AppState().getUser.data!.userInfo!.userLocalImage != null) { + AppState().getUser.data!.userInfo!.userLocalImage = null; + AppState().getUser.data!.userInfo!.userImageUrl = null; + } + navigateReplaceWithNameUntilRoute(context, AppRoutes.registerSelection); + } } diff --git a/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart b/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart index 1c9107d..0926a29 100644 --- a/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart +++ b/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart @@ -245,7 +245,7 @@ class AdDurationReview extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SingleDetailWidget(type: LocaleKeys.amount.tr(), text: adVM.specialServiceCards[index].serviceSelectedId!.itemPrice), + SingleDetailWidget(type: LocaleKeys.amountVAR.tr(), text: adVM.specialServiceCards[index].serviceSelectedId!.itemPrice), ], ), ), diff --git a/lib/views/advertisement/select_ad_type_view.dart b/lib/views/advertisement/select_ad_type_view.dart index fd9dc9b..22d9b0c 100644 --- a/lib/views/advertisement/select_ad_type_view.dart +++ b/lib/views/advertisement/select_ad_type_view.dart @@ -157,7 +157,7 @@ class SelectAdTypeView extends StatelessWidget { Row( children: [ "5 of 10 ".toText(fontSize: 29, isBold: true, letterSpacing: 0, height: 1), - LocaleKeys.adsRemaining.tr().toText(fontSize: 17, color: MyColors.lightTextColor, isBold: true), + LocaleKeys.adsRemainingVar.tr().toText(fontSize: 17, color: MyColors.lightTextColor, isBold: true), ], ), Text.rich( diff --git a/lib/views/appointments/review_appointment_view.dart b/lib/views/appointments/review_appointment_view.dart index 57b81c7..75e7e39 100644 --- a/lib/views/appointments/review_appointment_view.dart +++ b/lib/views/appointments/review_appointment_view.dart @@ -219,7 +219,7 @@ class ReviewAppointment extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.amount + LocaleKeys.amountVAR .tr() .toText(fontSize: 16, isBold: true), Row( diff --git a/lib/views/appointments/widgets/customer_appointment_slider_widget.dart b/lib/views/appointments/widgets/customer_appointment_slider_widget.dart index d6c0eaa..4374a5a 100644 --- a/lib/views/appointments/widgets/customer_appointment_slider_widget.dart +++ b/lib/views/appointments/widgets/customer_appointment_slider_widget.dart @@ -1,107 +1,148 @@ import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/view_models/appointments_view_model.dart'; import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/views/advertisement/custom_add_button.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; -//TODO: Need to make onTapped dynamic class CustomerAppointmentSliderWidget extends StatelessWidget { - final List myUpComingAppointments; - bool isNeedToShowEmptyMessage; - Function()? onAppointmentClick; + Function(AppointmentListModel)? onAppointmentClick; - CustomerAppointmentSliderWidget({Key? key, - required this.myUpComingAppointments, - this.isNeedToShowEmptyMessage = false, - this.onAppointmentClick}) - : super(key: key); + CustomerAppointmentSliderWidget({Key? key, this.onAppointmentClick}) : super(key: key); - @override - Widget build(BuildContext context) { - if (myUpComingAppointments.isEmpty) { - if (isNeedToShowEmptyMessage) - return "No Upcoming Appointment Available".toText().paddingAll(21); - return CustomAddButton( - needsBorder: true, - bgColor: MyColors.white, - onTap: () => context.read().onNavbarTapped(0), - text: "Add New Appointment", - icon: Container( - height: 24, - width: 24, - decoration: const BoxDecoration( - shape: BoxShape.circle, color: MyColors.darkTextColor), - child: const Icon(Icons.add, color: MyColors.white), - ), - ).padding(EdgeInsets.symmetric(vertical: 10, horizontal: 21)); - return Container( - height: 86, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - height: 24, - width: 24, - decoration: BoxDecoration( - shape: BoxShape.circle, color: MyColors.darkTextColor), - child: Icon( - Icons.add, - color: MyColors.white, - ), - ), - SizedBox(width: 10), - "Add New Appointment".toText( - fontSize: 15, - isBold: true, - color: MyColors.lightTextColor, - ), - ], - ), - ).onPress(() {}).toWhiteContainer( - width: double.infinity, - margin: EdgeInsets.symmetric(horizontal: 21, vertical: 10)); - } + Widget getCorouselWidget(AppType appType, AppointmentsVM appointmentsVM) { return CarouselSlider.builder( options: CarouselOptions( - height: 140, + height: appType == AppType.provider ? 110 : 140, viewportFraction: 1.0, enlargeCenterPage: false, enableInfiniteScroll: false, ), - itemCount: myUpComingAppointments.length, - itemBuilder: (BuildContext context, int itemIndex, int pageViewIndex) => - BuildAppointmentContainerForCustomer( - isForHome: true, - appointmentListModel: myUpComingAppointments[itemIndex], - onTapped: isNeedToShowEmptyMessage - ? onAppointmentClick! - : () => - navigateWithName(context, AppRoutes.appointmentDetailView, - arguments: myUpComingAppointments[itemIndex]), - ), + itemCount: appointmentsVM.myUpComingAppointments.length, + itemBuilder: (BuildContext context, int itemIndex, int pageViewIndex) => BuildAppointmentContainerForCustomer( + isForHome: appType == AppType.provider, + appointmentListModel: appointmentsVM.myUpComingAppointments[itemIndex], + onTapped: () { + if (appType == AppType.provider) { + onAppointmentClick!(appointmentsVM.myUpComingAppointments[itemIndex]); + } else { + navigateWithName(context, AppRoutes.appointmentDetailView, arguments: appointmentsVM.myUpComingAppointments[itemIndex]); + } + }, + ), ); } + + @override + Widget build(BuildContext context) { + return Consumer(builder: (BuildContext context, AppointmentsVM model, Widget? child) { + if (model.state == ViewState.busy) { + return const Center(child: CircularProgressIndicator()); + } else { + if (AppState().currentAppType == AppType.provider) { + if (model.myUpComingAppointments.isEmpty) { + return "No Upcoming Appointment Available".toText().paddingAll(21); + } else { + return getCorouselWidget(AppState().currentAppType, model); + } + } else { + if (model.myUpComingAppointments.isEmpty) { + return CustomAddButton( + needsBorder: true, + bgColor: MyColors.white, + onTap: () => context.read().onNavbarTapped(0), + text: "Add New Appointment", + icon: Container( + height: 24, + width: 24, + decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor), + child: const Icon(Icons.add, color: MyColors.white), + ), + ).padding(EdgeInsets.symmetric(vertical: 10, horizontal: 21)); + } else { + return getCorouselWidget(AppState().currentAppType, model); + } + } + } + }); + } + +// if (myUpComingAppointments.isEmpty) { +// if (isNeedToShowEmptyMessage) +// return "No Upcoming Appointment Available".toText().paddingAll(21); +// return CustomAddButton( +// needsBorder: true, +// bgColor: MyColors.white, +// onTap: () => context.read().onNavbarTapped(0), +// text: "Add New Appointment", +// icon: Container( +// height: 24, +// width: 24, +// decoration: const BoxDecoration( +// shape: BoxShape.circle, color: MyColors.darkTextColor), +// child: const Icon(Icons.add, color: MyColors.white), +// ), +// ).padding(EdgeInsets.symmetric(vertical: 10, horizontal: 21)); +// return Container( +// height: 86, +// child: Row( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// Container( +// height: 24, +// width: 24, +// decoration: BoxDecoration( +// shape: BoxShape.circle, color: MyColors.darkTextColor), +// child: Icon( +// Icons.add, +// color: MyColors.white, +// ), +// ), +// SizedBox(width: 10), +// "Add New Appointment".toText( +// fontSize: 15, +// isBold: true, +// color: MyColors.lightTextColor, +// ), +// ], +// ), +// ).onPress(() {}).toWhiteContainer( +// width: double.infinity, +// margin: EdgeInsets.symmetric(horizontal: 21, vertical: 10)); +// } +// return CarouselSlider.builder( +// options: CarouselOptions( +// height: 140, +// viewportFraction: 1.0, +// enlargeCenterPage: false, +// enableInfiniteScroll: false, +// ), +// itemCount: myUpComingAppointments.length, +// itemBuilder: (BuildContext context, int itemIndex, int pageViewIndex) => BuildAppointmentContainerForCustomer( +// isForHome: true, +// appointmentListModel: myUpComingAppointments[itemIndex], +// onTapped: isNeedToShowEmptyMessage ? onAppointmentClick! : () => navigateWithName(context, AppRoutes.appointmentDetailView, arguments: myUpComingAppointments[itemIndex]), +// ), +// ); } class BuildAppointmentContainerForCustomer extends StatelessWidget { final bool? isForHome; final AppointmentListModel? appointmentListModel; - final Function() onTapped; + final Function()? onTapped; - const BuildAppointmentContainerForCustomer({Key? key, - this.isForHome = false, - required this.onTapped, - required this.appointmentListModel}) - : super(key: key); + const BuildAppointmentContainerForCustomer({Key? key, this.isForHome = false, this.onTapped, required this.appointmentListModel}) : super(key: key); Widget showServices(String title, String icon, {bool isMoreText = false}) { return Row( @@ -121,18 +162,15 @@ class BuildAppointmentContainerForCustomer extends StatelessWidget { ); } - List buildServicesFromAppointment( - {required AppointmentListModel appointmentListModel}) { - if (appointmentListModel.appointmentServicesList == null || - appointmentListModel.appointmentServicesList!.isEmpty) { + List buildServicesFromAppointment({required AppointmentListModel appointmentListModel}) { + if (appointmentListModel.appointmentServicesList == null || appointmentListModel.appointmentServicesList!.isEmpty) { return [SizedBox()]; } if (appointmentListModel.appointmentServicesList!.length == 1) { return [ showServices( - appointmentListModel - .appointmentServicesList![0].providerServiceDescription, + appointmentListModel.appointmentServicesList![0].providerServiceDescription, MyAssets.modificationsIcon, ) ]; @@ -140,11 +178,7 @@ class BuildAppointmentContainerForCustomer extends StatelessWidget { List servicesList = List.generate( 2, - (index) => - showServices( - appointmentListModel - .appointmentServicesList![index].providerServiceDescription, - MyAssets.modificationsIcon), + (index) => showServices(appointmentListModel.appointmentServicesList![index].providerServiceDescription, MyAssets.modificationsIcon), ); if (appointmentListModel.appointmentServicesList!.length > 1) { @@ -170,33 +204,30 @@ class BuildAppointmentContainerForCustomer extends StatelessWidget { children: [ isForHome != null && isForHome! ? Image.asset( - MyAssets.bnCar, - width: 56, - height: 56, - fit: BoxFit.fill, - ).toCircle(borderRadius: 100) + MyAssets.bnCar, + width: 56, + height: 56, + fit: BoxFit.fill, + ).toCircle(borderRadius: 100) : Image.asset( - MyAssets.bnCar, - width: 80, - height: 85, - fit: BoxFit.cover, - ), + MyAssets.bnCar, + width: 80, + height: 85, + fit: BoxFit.cover, + ), 8.width, Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - (appointmentListModel!.providerName ?? "").toText( - color: MyColors.black, isBold: true, fontSize: 16), + (AppState().currentAppType == AppType.provider ? appointmentListModel!.customerName ?? "" : appointmentListModel!.branchName ?? "") + .toText(color: MyColors.black, isBold: true, fontSize: 16), Row( children: [ MyAssets.miniClock.buildSvg(height: 12), 2.width, - "${appointmentListModel!.duration ?? - ""} ${appointmentListModel!.appointmentDate! - .toFormattedDateWithoutTime()}" - .toText( + "${appointmentListModel!.duration ?? ""} ${appointmentListModel!.appointmentDate!.toFormattedDateWithoutTime()}".toText( color: MyColors.lightTextColor, fontSize: 12, ), @@ -205,42 +236,38 @@ class BuildAppointmentContainerForCustomer extends StatelessWidget { 9.height, isForHome != null && isForHome! ? Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - "Appointment Details".toText( - color: MyColors.primaryColor, - isUnderLine: true, - isBold: true, - fontSize: 14, - ), - const Icon(Icons.arrow_forward), - ], - ) + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + "Appointment Details".toText( + color: MyColors.primaryColor, + isUnderLine: true, + isBold: true, + fontSize: 14, + ), + const Icon(Icons.arrow_forward), + ], + ) : Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: Column( - children: buildServicesFromAppointment( - appointmentListModel: - appointmentListModel!), + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: Column( + children: buildServicesFromAppointment(appointmentListModel: appointmentListModel!), + ), + ), + const Icon( + Icons.arrow_forward, + ), + ], ), - ), - const Icon( - Icons.arrow_forward, - ), - ], - ), ], ), ), ], ), ], - ) - .onPress(onTapped) - .toWhiteContainer(width: double.infinity, allPading: 12), + ).onPress(onTapped!).toWhiteContainer(width: double.infinity, allPading: 12), ); } } diff --git a/lib/views/setting_options/provider_license_page.dart b/lib/views/setting_options/provider_license_page.dart index 7ae9c9c..2c10b16 100644 --- a/lib/views/setting_options/provider_license_page.dart +++ b/lib/views/setting_options/provider_license_page.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; @@ -34,8 +35,7 @@ class _ProviderLicensePageState extends State { // TODO: implement initState super.initState(); branchVM = Provider.of(context, listen: false); - branchVM.getServiceProviderDocument( - AppState().getUser.data!.userInfo!.providerId ?? 0); + branchVM.getServiceProviderDocument(AppState().getUser.data!.userInfo!.providerId ?? 0); } @override @@ -74,13 +74,14 @@ class _ProviderLicensePageState extends State { if (validation(model)) { updateDocument(model); } else { - Utils.showToast(LocaleKeys - .allDocumentMandatoryDealershipProvider - .tr()); + Utils.showToast(LocaleKeys.allDocumentMandatoryDealershipProvider.tr()); } } else { updateDocument(model); } + Future.delayed(const Duration(seconds: 1), () { + Navigator.of(context).pop(); + }); }, ), ), @@ -127,27 +128,19 @@ class _ProviderLicensePageState extends State { ), ), Padding( - padding: const EdgeInsets.only( - left: 20, right: 20, top: 4, bottom: 8), - child: LocaleKeys.enter_licence_detail.tr().toText( - fontSize: 14, - color: MyColors.lightTextColor, - textAlign: TextAlign.center), + padding: const EdgeInsets.only(left: 20, right: 20, top: 4, bottom: 8), + child: LocaleKeys.enter_licence_detail.tr().toText(fontSize: 14, color: MyColors.lightTextColor, textAlign: TextAlign.center), ), TxtField( hint: LocaleKeys.description.tr(), maxLines: 3, isBackgroundEnabled: true, ), - if ((model.document?.data![index].documentUrl ?? "") - .toString() - .isNotEmpty) + if ((model.document?.data![index].documentUrl ?? "").toString().isNotEmpty) Column( children: [ 8.height, - (model.document?.data![index].documentUrl ?? "") - .toString() - .toText( + (model.document?.data![index].documentUrl ?? "").toString().toText( fontSize: 14, color: MyColors.lightTextColor, ), @@ -163,10 +156,8 @@ class _ProviderLicensePageState extends State { height: 45, decoration: BoxDecoration( color: Colors.transparent, - border: - Border.all(color: MyColors.greyACColor, width: 2), - borderRadius: - const BorderRadius.all(Radius.circular(0)), + border: Border.all(color: MyColors.greyACColor, width: 2), + borderRadius: const BorderRadius.all(Radius.circular(0)), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/views/setting_options/setting_options_language.dart b/lib/views/setting_options/setting_options_language.dart index 69a5bea..22bf940 100644 --- a/lib/views/setting_options/setting_options_language.dart +++ b/lib/views/setting_options/setting_options_language.dart @@ -10,6 +10,7 @@ import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; +import 'package:mc_common_app/view_models/user_view_model.dart'; import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; @@ -40,8 +41,7 @@ class SettingOptionsLanguage extends StatelessWidget { Column( children: [ CustomSettingOptionsTile( - leadingWidget: - const Icon(Icons.quickreply_outlined, size: 20), + leadingWidget: const Icon(Icons.quickreply_outlined, size: 20), titleText: LocaleKeys.requests.tr(), needBorderBelow: true, onTap: () { @@ -56,12 +56,10 @@ class SettingOptionsLanguage extends StatelessWidget { //navigateWithName(context, AppRoutes.settingOptionsInviteFriends), ), CustomSettingOptionsTile( - leadingWidget: - const Icon(Icons.question_mark_outlined, size: 20), + leadingWidget: const Icon(Icons.question_mark_outlined, size: 20), titleText: LocaleKeys.help.tr(), needBorderBelow: true, - onTap: () => - navigateWithName(context, AppRoutes.settingOptionsHelp), + onTap: () => navigateWithName(context, AppRoutes.settingOptionsHelp), ), CustomSettingOptionsTile( leadingWidget: const Icon(Icons.person, size: 20), @@ -73,29 +71,25 @@ class SettingOptionsLanguage extends StatelessWidget { // Navigator.pop(context); }), ], - ).toWhiteContainer( - width: double.infinity, - pading: const EdgeInsets.all(12), - borderRadius: 0), + ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0), 10.height, CustomSettingOptionsTile( leadingWidget: const Icon(Icons.translate, size: 20), titleText: LocaleKeys.language.tr(), isForLanguage: true, onTap: () { - if (EasyLocalization.of(context) - ?.currentLocale - ?.countryCode == - "SA") { - context.setLocale(const Locale("en", "US")); - } else { - context.setLocale(const Locale('ar', 'SA')); - } + // if (EasyLocalization.of(context) + // ?.currentLocale + // ?.countryCode == + // "SA") { + // context.setLocale(const Locale("en", "US")); + // } else { + // context.setLocale(const Locale('ar', 'SA')); + // } + + context.read().changeLanguage(context); }, - ).toWhiteContainer( - width: double.infinity, - pading: const EdgeInsets.all(12), - borderRadius: 0), + ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0), 10.height, (AppState().currentAppType == AppType.provider) ? Column( @@ -109,13 +103,12 @@ class SettingOptionsLanguage extends StatelessWidget { color: MyColors.primaryColor, ), ), - titleText: LocaleKeys.mySubscriptions.tr(), - subTitle: "Silver", + titleText: LocaleKeys.mySubscription.tr(), + subTitle: AppState().getproviderSubscription.first.subscriptionName ?? "Silver", isForLanguage: false, needBorderBelow: true, onTap: () { - navigateWithName( - context, AppRoutes.mySubscriptionsPage); + navigateWithName(context, AppRoutes.mySubscriptionsPage); }, ), CustomSettingOptionsTile( @@ -132,8 +125,7 @@ class SettingOptionsLanguage extends StatelessWidget { isForLanguage: false, needBorderBelow: true, onTap: () { - navigateWithName( - context, AppRoutes.subscriptionsPage); + navigateWithName(context, AppRoutes.subscriptionsPage); }, ), CustomSettingOptionsTile( @@ -150,19 +142,13 @@ class SettingOptionsLanguage extends StatelessWidget { isForLanguage: false, needBorderBelow: false, onTap: () { - navigateWithName( - context, AppRoutes.providerLicensePage); + navigateWithName(context, AppRoutes.providerLicensePage); }, ) ], - ).toWhiteContainer( - width: double.infinity, - pading: const EdgeInsets.all(12), - borderRadius: 0) - : SizedBox(), - (AppState().currentAppType == AppType.provider) - ? 10.height + ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0) : SizedBox(), + (AppState().currentAppType == AppType.provider) ? 10.height : SizedBox(), ], )), // : Expanded( @@ -188,9 +174,7 @@ class SettingOptionsLanguage extends StatelessWidget { // ], // ), // ), - (AppState().currentAppType == AppType.provider) - ? Text(LocaleKeys.provider.tr()) - : Text(LocaleKeys.customer.tr()), + (AppState().currentAppType == AppType.provider) ? Text(LocaleKeys.provider.tr()) : Text(LocaleKeys.customer.tr()), Row( children: [ Expanded( @@ -201,7 +185,9 @@ class SettingOptionsLanguage extends StatelessWidget { fontSize: 16, maxHeight: 55, title: LocaleKeys.logOut.tr(), - onPressed: () {}, + onPressed: () { + context.read().logout(context); + }, ), ), ], diff --git a/lib/views/user/login_with_password_page.dart b/lib/views/user/login_with_password_page.dart index 01f279c..c6f1bb3 100644 --- a/lib/views/user/login_with_password_page.dart +++ b/lib/views/user/login_with_password_page.dart @@ -46,13 +46,14 @@ class _LoginWithPasswordState extends State { void initState() { super.initState(); scheduleMicrotask(() { - if (AppState().currentAppType == AppType.provider) { - phoneNum = "966530896018"; - password = "Amir@1234"; - } userVM = Provider.of(context, listen: false); context.read().getAvailBio(); - + if (AppState().currentAppType == AppType.provider) { + // phoneNum = "966530896018"; + // password = "Amir@1234"; + phoneNum = "966569755630"; + password = "Amir12345@"; + } getCountryList(); }); diff --git a/lib/widgets/common_widgets/app_bar.dart b/lib/widgets/common_widgets/app_bar.dart index 340bfa1..0dec6b6 100644 --- a/lib/widgets/common_widgets/app_bar.dart +++ b/lib/widgets/common_widgets/app_bar.dart @@ -42,6 +42,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { + // print("User Image URL == ${AppState().getUser.data!.userInfo!.userImageUrl} ============"); return Column( children: [ AppBar( @@ -64,27 +65,24 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { ).toCircle(borderRadius: 100) : profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userImageUrl != null ? CachedNetworkImage( - imageUrl: AppState().getUser.data!.userInfo!.userImageUrl, - imageBuilder: (context, imageProvider) => - Container( - decoration: BoxDecoration( - image: DecorationImage( - image: imageProvider, - fit: BoxFit.cover, + imageUrl: AppState().getUser.data!.userInfo!.userImageUrl, + imageBuilder: (context, imageProvider) => + Container( + decoration: BoxDecoration( + image: DecorationImage( + image: imageProvider, + fit: BoxFit.cover, + ), ), ), - ), - placeholder: (context, url) => const Center(child: CircularProgressIndicator()), - errorWidget: (context, url, error) => const Icon(Icons.error), - fadeInCurve: Curves.easeIn, - width: 34, - height: 34, - fit: BoxFit.fill, - - fadeInDuration: Duration(milliseconds: 1000), - useOldImageOnUrlChange: false, - - + placeholder: (context, url) => const Center(child: CircularProgressIndicator()), + errorWidget: (context, url, error) => const Icon(Icons.supervised_user_circle_outlined), + fadeInCurve: Curves.easeIn, + width: 34, + height: 34, + fit: BoxFit.fill, + fadeInDuration: Duration(milliseconds: 1000), + useOldImageOnUrlChange: false ).toCircle(borderRadius: 100) : Image.asset( MyAssets.carBanner, diff --git a/lib/widgets/tab/menu_tabs.dart b/lib/widgets/tab/menu_tabs.dart index 88e12d1..743719f 100644 --- a/lib/widgets/tab/menu_tabs.dart +++ b/lib/widgets/tab/menu_tabs.dart @@ -3,19 +3,13 @@ import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; -class MenuTabs extends StatefulWidget { - int selectedIndex; +class MenuTabs extends StatelessWidget { List dropList; - Function(DropValue value) onSelect; + Function(DropValue value, int index ) onSelected; Color? selectedColor; - MenuTabs(this.selectedIndex, this.dropList, {required this.onSelect, this.selectedColor}); + MenuTabs(this.dropList, {required this.onSelected, this.selectedColor}); - @override - State createState() => _RoleTypeTabState(); -} - -class _RoleTypeTabState extends State { @override Widget build(BuildContext context) { return SizedBox( @@ -25,24 +19,21 @@ class _RoleTypeTabState extends State { itemBuilder: (context, index) { return InkWell( onTap: () { - setState(() { - widget.selectedIndex = index; - widget.onSelect(widget.dropList[index]); - }); + onSelected(dropList[index], index); }, child: Container( height: 45, decoration: BoxDecoration( - color: widget.selectedIndex == index ? widget.selectedColor ?? MyColors.darkIconColor : Colors.white, - border: Border.all(color: widget.selectedIndex == index ? widget.selectedColor ?? MyColors.darkIconColor : MyColors.darkPrimaryColor, width: 1.5), + color: dropList[index].isEnabled! ? selectedColor ?? MyColors.darkIconColor : Colors.white, + border: Border.all(color: dropList[index].isEnabled! ? selectedColor ?? MyColors.darkIconColor : MyColors.darkPrimaryColor, width: 1.5), borderRadius: const BorderRadius.all(Radius.circular(0)), ), padding: const EdgeInsets.symmetric(horizontal: 20), child: Center( child: Text( - widget.dropList[index].value, + dropList[index].value, style: TextStyle( - color: widget.selectedIndex == index ? MyColors.white : Colors.black, + color: dropList[index].isEnabled! ? MyColors.white : Colors.black, fontSize: 12, fontWeight: FontWeight.w600, ), @@ -55,7 +46,7 @@ class _RoleTypeTabState extends State { return 12.width; }, padding: const EdgeInsets.symmetric(horizontal: 21), - itemCount: widget.dropList.length, + itemCount: dropList.length, scrollDirection: Axis.horizontal, ), ); diff --git a/lib/widgets/tab/role_type_tab.dart b/lib/widgets/tab/role_type_tab.dart index a3983cf..f26f789 100644 --- a/lib/widgets/tab/role_type_tab.dart +++ b/lib/widgets/tab/role_type_tab.dart @@ -20,7 +20,7 @@ class _RoleTypeTabState extends State { Widget build(BuildContext context) { return SizedBox( width: double.infinity, - height: 45, + height: 50, child: ListView.separated( itemBuilder: (context, index) { return InkWell( @@ -32,7 +32,7 @@ class _RoleTypeTabState extends State { }, child: Container( width: widget.width ?? (MediaQuery.of(context).size.width / 2) - 30, - height: 45, + height: 50, decoration: BoxDecoration( color: widget.selectedIndex == index ? MyColors.darkPrimaryColor : Colors.grey[200], // border: Border.all(color: type == ClassType.NUMBER ? MyColors.darkPrimaryColor : Colors.transparent, width: 2), @@ -43,13 +43,16 @@ class _RoleTypeTabState extends State { child: Center( child: Text( (widget.dropList[index].value) == "ServiceProvider_Dealership" || (widget.dropList[index].value) == "ServiceProvider_Individual" - ? widget.dropList[index].value.split("_").first + "\n" + widget.dropList[index].value.split("_").last + ? "${widget.dropList[index].value.split("_").first}\n${widget.dropList[index].value.split("_").last}" : widget.dropList[index].value, textAlign: TextAlign.center, + style: TextStyle( color: widget.selectedIndex == index ? MyColors.white : Colors.black, fontSize: 15, + height: 1 ), + ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index a85049c..4e176a9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,7 +47,7 @@ dependencies: google_maps_flutter: ^2.1.1 geolocator: any # geocoding: ^2.1.0 - geocoding: ^2.1.1 + geocoding: ^3.0.0 # Auth @@ -73,10 +73,15 @@ flutter: - assets/icons/ - assets/icons/payments/ - assets/images/ - - assets/fonts/Poppins-Medium.ttf + - assets/fonts/ fonts: - family: Poppins fonts: - asset: assets/fonts/Poppins-Medium.ttf + - family: GessTwo + fonts: + - asset: assets/fonts/GESS-Two-Light.otf + - asset: assets/fonts/GESS-Two-Bold.otf + - asset: assets/fonts/GESS-Two-Medium.otf