From 13c28a14f155f840281fe8a953194ce5ecc17cb4 Mon Sep 17 00:00:00 2001 From: "Aamir.Muhammad" <> Date: Tue, 23 Jul 2024 16:31:19 +0300 Subject: [PATCH 1/4] appointment slider & subscription fix --- .../provider_subscription_model.dart | 85 ++++++ lib/repositories/appointment_repo.dart | 2 +- lib/repositories/subscription_repo.dart | 115 ++++---- lib/view_models/appointments_view_model.dart | 249 ++++++++---------- lib/view_models/service_view_model.dart | 36 ++- lib/view_models/subscriptions_view_model.dart | 20 +- lib/view_models/user_view_model.dart | 15 +- .../customer_appointment_slider_widget.dart | 210 ++++++++------- lib/views/user/login_with_password_page.dart | 5 +- pubspec.yaml | 2 +- 10 files changed, 422 insertions(+), 317 deletions(-) create mode 100644 lib/models/subscriptions_models/provider_subscription_model.dart 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..46a5c40 --- /dev/null +++ b/lib/models/subscriptions_models/provider_subscription_model.dart @@ -0,0 +1,85 @@ +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; + + 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 + }); + + 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"], + ); + + 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, + }; +} 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/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 33496d8..0cf8865 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -45,11 +45,10 @@ class AppointmentsVM extends BaseVM { final BranchRepo branchRepo; final AppointmentRepo appointmentRepo; - AppointmentsVM( - {required this.commonServices, - required this.appointmentRepo, - required this.commonRepo, - required this.branchRepo}); + AppointmentsVM({required this.commonServices, + required this.appointmentRepo, + required this.commonRepo, + required this.branchRepo}); bool isUpcommingEnabled = true; bool isFetchingLists = false; @@ -72,6 +71,7 @@ class AppointmentsVM extends BaseVM { List branchCategories = []; bool isHomeTapped = false; + bool isShowEmptyMessage = false; List serviceAppointmentScheduleList = []; @@ -99,7 +99,7 @@ class AppointmentsVM extends BaseVM { Future onItemsSelectedInService() async { if (currentServiceSelection != null) { int index = servicesInCurrentAppointment.indexWhere((element) => - element.serviceId == currentServiceSelection!.serviceId!); + element.serviceId == currentServiceSelection!.serviceId!); if (index == -1) { double totalPrice = 0.0; @@ -115,8 +115,7 @@ class AppointmentsVM extends BaseVM { } } - Future onPayNowPressedForAppointment( - {required BuildContext context, required int appointmentID}) async { + Future onPayNowPressedForAppointment({required BuildContext context, required int appointmentID}) async { context .read() .updateAppointmentIdsForPayment(ids: [appointmentID]); @@ -130,7 +129,7 @@ class AppointmentsVM extends BaseVM { List appointmentIdsList = []; try { GenericRespModel genericRespModel = - await appointmentRepo.createServiceAppointment( + await appointmentRepo.createServiceAppointment( schedules: serviceAppointmentScheduleList, serviceProviderID: selectedBranchModel!.serviceProviderId ?? 0, ); @@ -178,8 +177,7 @@ class AppointmentsVM extends BaseVM { } } - Future onConfirmAppointmentPressed( - {required BuildContext context, required appointmentId}) async { + Future onConfirmAppointmentPressed({required BuildContext context, required appointmentId}) async { context .read() .updateAppointmentIdsForPayment(ids: [appointmentId]); @@ -187,13 +185,12 @@ class AppointmentsVM extends BaseVM { arguments: PaymentTypes.appointment); } - Future onCancelAppointmentPressed( - {required BuildContext context, - required AppointmentListModel appointmentListModel}) async { + Future onCancelAppointmentPressed({required BuildContext context, + required AppointmentListModel appointmentListModel}) async { Utils.showLoading(context); try { GenericRespModel genericRespModel = - await appointmentRepo.cancelOrRescheduleServiceAppointment( + await appointmentRepo.cancelOrRescheduleServiceAppointment( serviceAppointmentID: appointmentListModel.id ?? 0, serviceSlotID: appointmentListModel.serviceSlotID ?? 0, appointmentScheduleAction: 2, // 1 for Reschedule and 2 for Cancel @@ -240,7 +237,7 @@ class AppointmentsVM extends BaseVM { } SelectionModel branchSelectedCategoryId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateProviderCategoryId(SelectionModel id) { branchSelectedCategoryId = id; @@ -256,18 +253,18 @@ class AppointmentsVM extends BaseVM { void updateBranchServiceId(SelectionModel id) async { branchSelectedServiceId = id; currentServiceSelection = branchServices.firstWhere( - (element) => element.serviceProviderServiceId == id.selectedId); + (element) => element.serviceProviderServiceId == id.selectedId); notifyListeners(); } void removeServiceInCurrentAppointment(int index) { int serviceId = servicesInCurrentAppointment - .elementAt(index) - .serviceProviderServiceId ?? + .elementAt(index) + .serviceProviderServiceId ?? -1; allSelectedItemsInAppointments.removeWhere( - (element) => element.serviceProviderServiceId == serviceId); + (element) => element.serviceProviderServiceId == serviceId); servicesInCurrentAppointment.removeAt(index); notifyListeners(); } @@ -314,9 +311,8 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - applyFilterOnAppointmentsVM( - {required AppointmentStatusEnum appointmentStatusEnum, - bool isNeedCustomerFilter = false}) { + applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum, + bool isNeedCustomerFilter = false}) { // isNeedCustomerFilter IS ONLY FOR THE PROVIDER APP if (appointmentsFilterOptions.isEmpty) return; for (var value in appointmentsFilterOptions) { @@ -336,17 +332,15 @@ class AppointmentsVM extends BaseVM { if (appointmentStatusEnum.getIdFromAppointmentStatusEnum() == 0) { myFilteredAppointments = myAppointments; if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers(); - notifyListeners(); return; } myFilteredAppointments = myAppointments .where((element) => - element.appointmentStatusID! == - appointmentStatusEnum.getIdFromAppointmentStatusEnum()) + element.appointmentStatusID! == + appointmentStatusEnum.getIdFromAppointmentStatusEnum()) .toList(); if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers(); - notifyListeners(); } findAppointmentsBasedOnCustomers() { @@ -382,32 +376,35 @@ class AppointmentsVM extends BaseVM { // }).toList(); } - Future getMyAppointments({bool isNeedToRebuild = false}) async { - if (isNeedToRebuild) setState(ViewState.busy); + Future getMyAppointments() async { + setState(ViewState.busy); myAppointments = - await appointmentRepo.getMyAppointmentsForCustomersByFilters(); - // myFilteredAppointments = myAppointments; + await appointmentRepo.getMyAppointmentsForCustomersByFilters(); + myFilteredAppointments = myAppointments; myUpComingAppointments = myAppointments .where((element) => - element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) + element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) .toList(); - setState(ViewState.idle); + applyFilterOnAppointmentsVM( appointmentStatusEnum: AppointmentStatusEnum.allAppointments); + if (myUpComingAppointments.isEmpty) { + isShowEmptyMessage = true; + } + setState(ViewState.idle); notifyListeners(); } AppointmentSlots? appointmentSlots; - Future getAppointmentSlotsInfo( - {required Map map, - required BuildContext context, - bool isNeedToRebuild = false}) async { + Future getAppointmentSlotsInfo({required Map map, + required BuildContext context, + bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); try { MResponse genericRespModel = - await appointmentRepo.getAppointmentSlots(map); + await appointmentRepo.getAppointmentSlots(map); if (genericRespModel.messageStatus == 1) { appointmentSlots = AppointmentSlots.fromJson(genericRespModel.data); } else { @@ -418,17 +415,14 @@ 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 + myUpComingAppointments = await myAppointments .where((element) => - element.appointmentStatusEnum == AppointmentStatusEnum.booked) + element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) .toList(); - applyFilterOnAppointmentsVM( appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true); @@ -440,7 +434,7 @@ class AppointmentsVM extends BaseVM { if (isNeedToRebuild) setState(ViewState.busy); try { MResponse genericRespModel = - await appointmentRepo.updateAppointmentStatus(map); + await appointmentRepo.updateAppointmentStatus(map); if (genericRespModel.messageStatus == 1) { Utils.showToast(LocaleKeys.appointmentStatusUpdated.tr()); @@ -457,7 +451,7 @@ class AppointmentsVM extends BaseVM { if (isNeedToRebuild) setState(ViewState.busy); try { MResponse genericRespModel = - await appointmentRepo.updateAppointmentPaymentStatus(map); + await appointmentRepo.updateAppointmentPaymentStatus(map); if (genericRespModel.messageStatus == 1) { Utils.showToast(LocaleKeys.paymentStatusUpdated.tr()); @@ -473,7 +467,7 @@ class AppointmentsVM extends BaseVM { {bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); MResponse genericRespModel = - await appointmentRepo.createMergeAppointment(map); + await appointmentRepo.createMergeAppointment(map); return genericRespModel; } @@ -484,12 +478,12 @@ class AppointmentsVM extends BaseVM { myFilteredAppointments2[selectedAppointmentIndex] .customerAppointmentList![currentIndex] .isSelected = !(myFilteredAppointments2[selectedAppointmentIndex] - .customerAppointmentList?[currentIndex] - .isSelected ?? + .customerAppointmentList?[currentIndex] + .isSelected ?? false); int count = countSelected(myFilteredAppointments2[selectedAppointmentIndex] - .customerAppointmentList ?? + .customerAppointmentList ?? []); if (count > 1) inNeedToEnableMergeButton = true; @@ -505,8 +499,7 @@ class AppointmentsVM extends BaseVM { .length; } - updateSelectedAppointmentDate( - {required int dateIndex, required int scheduleIndex}) { + updateSelectedAppointmentDate({required int dateIndex, required int scheduleIndex}) { for (var element in serviceAppointmentScheduleList[scheduleIndex] .customTimeDateSlotList!) { element.date!.isSelected = false; @@ -534,8 +527,7 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - updateSelectedAppointmentSlotByDate( - {required int scheduleIndex, required int slotIndex}) { + updateSelectedAppointmentSlotByDate({required int scheduleIndex, required int slotIndex}) { for (var element in serviceAppointmentScheduleList[scheduleIndex] .customTimeDateSlotList!) { for (var element in element.availableSlots!) { @@ -543,17 +535,17 @@ class AppointmentsVM extends BaseVM { } } int index = - serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!; + serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!; serviceAppointmentScheduleList[scheduleIndex] .customTimeDateSlotList![index] .availableSlots![slotIndex] .isSelected = true; serviceAppointmentScheduleList[scheduleIndex] - .selectedCustomTimeDateSlotModel! - .availableSlots = - serviceAppointmentScheduleList[scheduleIndex] - .customTimeDateSlotList![index] - .availableSlots!; + .selectedCustomTimeDateSlotModel! + .availableSlots = + serviceAppointmentScheduleList[scheduleIndex] + .customTimeDateSlotList![index] + .availableSlots!; notifyListeners(); } @@ -568,7 +560,7 @@ class AppointmentsVM extends BaseVM { onItemUpdateOrSelected(int index, bool selected, int itemId) { int serviceIndex = servicesInCurrentAppointment.indexWhere( - (element) => element.serviceId == currentServiceSelection!.serviceId!); + (element) => element.serviceId == currentServiceSelection!.serviceId!); // print("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}"); // if (serviceIndex == -1) { // return; @@ -588,7 +580,7 @@ class AppointmentsVM extends BaseVM { .add(serviceItemsFromApi[index]); servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex] - .currentTotalServicePrice + + .currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0"); } } @@ -637,7 +629,7 @@ class AppointmentsVM extends BaseVM { String selectSubServicesError = ""; SelectionModel branchSelectedServiceId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updatePickHomeLocationError(String value) { pickHomeLocationError = value; @@ -677,8 +669,7 @@ class AppointmentsVM extends BaseVM { return totalPrice.toString(); } - void openTheAddServiceBottomSheet( - BuildContext context, AppointmentsVM appointmentsVM) { + void openTheAddServiceBottomSheet(BuildContext context, AppointmentsVM appointmentsVM) { showModalBottomSheet( context: context, isScrollControlled: true, @@ -689,8 +680,7 @@ class AppointmentsVM extends BaseVM { ); } - void priceBreakDownClicked( - BuildContext context, ServiceModel selectedService) { + void priceBreakDownClicked(BuildContext context, ServiceModel selectedService) { showModalBottomSheet( context: context, isScrollControlled: true, @@ -708,17 +698,18 @@ 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( @@ -764,16 +755,16 @@ class AppointmentsVM extends BaseVM { crossAxisAlignment: CrossAxisAlignment.end, children: [ (selectedService.isHomeSelected - ? "${(selectedService.currentTotalServicePrice) + (double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms)}" - : "${selectedService.currentTotalServicePrice}") + ? "${(selectedService.currentTotalServicePrice) + (double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms)}" + : "${selectedService.currentTotalServicePrice}") .toText(fontSize: 29, isBold: true), 2.width, LocaleKeys.sar .tr() .toText( - color: MyColors.lightTextColor, - fontSize: 16, - isBold: true) + color: MyColors.lightTextColor, + fontSize: 16, + isBold: true) .paddingOnly(bottom: 5), ], ) @@ -834,7 +825,7 @@ class AppointmentsVM extends BaseVM { } serviceAppointmentScheduleList = - await appointmentRepo.mergeServiceIntoAvailableSchedules( + await appointmentRepo.mergeServiceIntoAvailableSchedules( serviceItemIdsForHome: serviceItemIdsForHome, serviceItemIdsForWorkshop: serviceItemIdsForWorkshop, ); @@ -860,9 +851,8 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - Future onRescheduleAppointmentPressed( - {required BuildContext context, - required AppointmentListModel appointmentListModel}) async { + Future onRescheduleAppointmentPressed({required BuildContext context, + required AppointmentListModel appointmentListModel}) async { Utils.showLoading(context); List serviceItemIdsForHome = []; @@ -880,7 +870,7 @@ class AppointmentsVM extends BaseVM { } serviceAppointmentScheduleList = - await appointmentRepo.mergeServiceIntoAvailableSchedules( + await appointmentRepo.mergeServiceIntoAvailableSchedules( serviceItemIdsForHome: serviceItemIdsForHome, serviceItemIdsForWorkshop: serviceItemIdsForWorkshop, ); @@ -902,14 +892,13 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - Future onRescheduleAppointmentConfirmPressed( - {required BuildContext context, - required int appointmentId, - required int selectedSlotId}) async { + Future onRescheduleAppointmentConfirmPressed({required BuildContext context, + required int appointmentId, + required int selectedSlotId}) async { Utils.showLoading(context); try { GenericRespModel genericRespModel = - await appointmentRepo.cancelOrRescheduleServiceAppointment( + await appointmentRepo.cancelOrRescheduleServiceAppointment( serviceAppointmentID: appointmentId, serviceSlotID: selectedSlotId, appointmentScheduleAction: 1, // 1 for Reschedule and 2 for Cancel @@ -972,14 +961,13 @@ class AppointmentsVM extends BaseVM { setState(ViewState.idle); } - Future getAllNearBranches( - {bool isNeedToRebuild = false, bool isFromRefresh = false}) async { + Future getAllNearBranches({bool isNeedToRebuild = false, bool isFromRefresh = false}) async { nearbyBranches.clear(); if (isNeedToRebuild) setState(ViewState.busy); if (isFromRefresh) { var selectedBranch = - branchesFilterOptions.firstWhere((element) => element.isSelected); + branchesFilterOptions.firstWhere((element) => element.isSelected); nearbyBranches = await branchRepo .getBranchesByFilters(categoryIdsList: [selectedBranch.id]); setState(ViewState.idle); @@ -1023,7 +1011,7 @@ class AppointmentsVM extends BaseVM { getBranchAndServices(int providerId) async { providerProfileModel = null; providerProfileModel = - await branchRepo.getBranchAndServicesByProviderId(providerId); + await branchRepo.getBranchAndServicesByProviderId(providerId); setState(ViewState.idle); } @@ -1058,8 +1046,7 @@ class AppointmentsVM extends BaseVM { // Provider Filter List branchFilterProviderSearchHistory = []; - void removeBranchFilterProviderSearchHistory( - {bool isClear = false, required int index}) { + void removeBranchFilterProviderSearchHistory({bool isClear = false, required int index}) { if (isClear) { branchFilterProviderSearchHistory.clear(); notifyListeners(); @@ -1082,7 +1069,7 @@ class AppointmentsVM extends BaseVM { } SelectionModel branchFilterSelectedProviderId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateBranchFilterSelectedProviderId(SelectionModel id, {bool isForSearch = false}) async { @@ -1102,8 +1089,7 @@ class AppointmentsVM extends BaseVM { // Category Filter List branchFilterCategorySearchHistory = []; - void removeBranchFilterCategorySearchHistory( - {bool isClear = false, required int index}) { + void removeBranchFilterCategorySearchHistory({bool isClear = false, required int index}) { if (isClear) { branchFilterCategorySearchHistory.clear(); notifyListeners(); @@ -1126,13 +1112,13 @@ class AppointmentsVM extends BaseVM { } SelectionModel branchFilterSelectedCategoryId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateBranchFilterSelectedCategoryId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { DropValue categoryDrop = - categoryDropList.firstWhere((element) => element.id == id.selectedId); + categoryDropList.firstWhere((element) => element.id == id.selectedId); if (!ifAlreadyExist( list: branchFilterCategorySearchHistory, value: categoryDrop)) { addBranchFilterCategorySearchHistory(value: categoryDrop); @@ -1146,8 +1132,7 @@ class AppointmentsVM extends BaseVM { // Services Filter List branchFilterServicesSearchHistory = []; - void removeBranchFilterServicesSearchHistory( - {bool isClear = false, required int index}) { + void removeBranchFilterServicesSearchHistory({bool isClear = false, required int index}) { if (isClear) { branchFilterServicesSearchHistory.clear(); notifyListeners(); @@ -1170,13 +1155,13 @@ class AppointmentsVM extends BaseVM { } SelectionModel branchFilterSelectedServiceId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateBranchFilterSelectedServiceId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { DropValue serviceDrop = - servicesDropList.firstWhere((element) => element.id == id.selectedId); + servicesDropList.firstWhere((element) => element.id == id.selectedId); if (!ifAlreadyExist( list: branchFilterServicesSearchHistory, value: serviceDrop)) { addBranchFilterServicesSearchHistory(value: serviceDrop); @@ -1220,7 +1205,7 @@ class AppointmentsVM extends BaseVM { providersDropList.clear(); setOnlyState(ViewState.busy); List providers = - await branchRepo.getAllProvidersWitheBasicData(); + await branchRepo.getAllProvidersWitheBasicData(); for (var element in providers) { providersDropList.add( DropValue(element.id ?? 0, element.providerName ?? "N/A", ""), @@ -1239,10 +1224,10 @@ class AppointmentsVM 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"), ""), ); @@ -1330,8 +1315,7 @@ class AppointmentsVM extends BaseVM { setState(ViewState.idle); } - Future getBranchesBasedOnCategoryFilters( - {required int categoryId}) async { + Future getBranchesBasedOnCategoryFilters({required int categoryId}) async { if (categoryId == 0) { await getAllNearBranches(); return; @@ -1339,7 +1323,7 @@ class AppointmentsVM extends BaseVM { setState(ViewState.busy); nearbyBranches.clear(); nearbyBranches = - await branchRepo.getBranchesByFilters(categoryIdsList: [categoryId]); + await branchRepo.getBranchesByFilters(categoryIdsList: [categoryId]); setState(ViewState.idle); } @@ -1347,8 +1331,7 @@ class AppointmentsVM extends BaseVM { List branchesDropList = []; - Future fetchAllBranchesBySelectedProviderId( - {required List providersIdsList}) async { + Future fetchAllBranchesBySelectedProviderId({required List providersIdsList}) async { branchesDropList.clear(); setOnlyState(ViewState.busy); List providers = await branchRepo.getBranchesByFilters( @@ -1378,8 +1361,7 @@ class AppointmentsVM extends BaseVM { // Provider Filter For Appointments List appointmentFilterProviderSearchHistory = []; - void removeAppointmentFilterProviderSearchHistory( - {bool isClear = false, required int index}) { + void removeAppointmentFilterProviderSearchHistory({bool isClear = false, required int index}) { if (isClear) { appointmentFilterProviderSearchHistory.clear(); notifyListeners(); @@ -1416,7 +1398,7 @@ class AppointmentsVM extends BaseVM { } SelectionModel appointmentFilterSelectedProviderId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateAppointmentFilterSelectedProviderId(SelectionModel id, {bool isForSearch = false}) async { @@ -1436,8 +1418,7 @@ class AppointmentsVM extends BaseVM { List appointmentFilterBranchSearchHistory = []; - void removeAppointmentFilterBranchSearchHistory( - {bool isClear = false, required int index}) { + void removeAppointmentFilterBranchSearchHistory({bool isClear = false, required int index}) { if (isClear) { appointmentFilterBranchSearchHistory.clear(); notifyListeners(); @@ -1460,13 +1441,13 @@ class AppointmentsVM extends BaseVM { } SelectionModel appointmentFilterSelectedBranchId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateAppointmentFilterSelectedBranchId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { DropValue branchesDrop = - branchesDropList.firstWhere((element) => element.id == id.selectedId); + branchesDropList.firstWhere((element) => element.id == id.selectedId); if (!ifAlreadyExist( list: appointmentFilterBranchSearchHistory, value: branchesDrop)) { addAppointmentFilterBranchSearchHistory(value: branchesDrop); @@ -1480,8 +1461,7 @@ class AppointmentsVM extends BaseVM { List appointmentFilterCategorySearchHistory = []; - void removeAppointmentFilterCategorySearchHistory( - {bool isClear = false, required int index}) { + void removeAppointmentFilterCategorySearchHistory({bool isClear = false, required int index}) { if (isClear) { appointmentFilterCategorySearchHistory.clear(); notifyListeners(); @@ -1504,13 +1484,13 @@ class AppointmentsVM extends BaseVM { } SelectionModel appointmentFilterSelectedCategoryId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateAppointmentFilterSelectedCategoryId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { DropValue categoryDrop = - categoryDropList.firstWhere((element) => element.id == id.selectedId); + categoryDropList.firstWhere((element) => element.id == id.selectedId); if (!ifAlreadyExist( list: appointmentFilterCategorySearchHistory, value: categoryDrop)) { addAppointmentFilterCategorySearchHistory(value: categoryDrop); @@ -1522,8 +1502,7 @@ class AppointmentsVM extends BaseVM { List appointmentFilterServicesSearchHistory = []; - void removeAppointmentFilterServicesSearchHistory( - {bool isClear = false, required int index}) { + void removeAppointmentFilterServicesSearchHistory({bool isClear = false, required int index}) { if (isClear) { appointmentFilterServicesSearchHistory.clear(); notifyListeners(); @@ -1546,13 +1525,13 @@ class AppointmentsVM extends BaseVM { } SelectionModel appointmentFilterSelectedServiceId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateAppointmentFilterSelectedServiceId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { DropValue servicesDrop = - servicesDropList.firstWhere((element) => element.id == id.selectedId); + servicesDropList.firstWhere((element) => element.id == id.selectedId); if (!ifAlreadyExist( list: appointmentFilterServicesSearchHistory, value: servicesDrop)) { addAppointmentFilterServicesSearchHistory(value: servicesDrop); @@ -1612,7 +1591,7 @@ class AppointmentsVM extends BaseVM { } myAppointments = - await appointmentRepo.getMyAppointmentsForCustomersByFilters( + await appointmentRepo.getMyAppointmentsForCustomersByFilters( providerIdsList: providersIdsList.isNotEmpty ? providersIdsList : null, categoryIdsList: categoryIdsList.isNotEmpty ? categoryIdsList : null, serviceIdsList: servicesIdsList.isNotEmpty ? servicesIdsList : null, 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..546ed13 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 @@ -110,17 +112,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; @@ -175,4 +172,13 @@ 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); + } + setState(ViewState.idle); + } } diff --git a/lib/view_models/user_view_model.dart b/lib/view_models/user_view_model.dart index 27e45d6..34b65f2 100644 --- a/lib/view_models/user_view_model.dart +++ b/lib/view_models/user_view_model.dart @@ -350,13 +350,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 +367,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) { @@ -381,17 +377,17 @@ class UserVM extends BaseVM { SharedPrefManager.setData(jsonEncode(user.data!.userInfo!.toJson())); navigateReplaceWithName(context, AppRoutes.dashboard); } else { - Utils.showToast(LocaleKeys.onlyCustomerApp.tr()); + Utils.showToast(LocaleKeys.onlyCustomerApp.tr()); } } } else { - Utils.showToast(verifiedUser.message ?? ""); + Utils.showToast(verifiedUser.message ?? ""); } } } 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 { @@ -628,6 +624,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); } diff --git a/lib/views/appointments/widgets/customer_appointment_slider_widget.dart b/lib/views/appointments/widgets/customer_appointment_slider_widget.dart index d6c0eaa..5eb7be0 100644 --- a/lib/views/appointments/widgets/customer_appointment_slider_widget.dart +++ b/lib/views/appointments/widgets/customer_appointment_slider_widget.dart @@ -1,107 +1,149 @@ 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: 110, viewportFraction: 1.0, enlargeCenterPage: false, enableInfiniteScroll: false, ), - itemCount: myUpComingAppointments.length, + itemCount: appointmentsVM.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]), + 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 +163,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 +179,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) { @@ -187,16 +222,13 @@ class BuildAppointmentContainerForCustomer extends StatelessWidget { 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, ), @@ -222,9 +254,7 @@ class BuildAppointmentContainerForCustomer extends StatelessWidget { children: [ Expanded( child: Column( - children: buildServicesFromAppointment( - appointmentListModel: - appointmentListModel!), + children: buildServicesFromAppointment(appointmentListModel: appointmentListModel!), ), ), const Icon( @@ -238,9 +268,7 @@ class BuildAppointmentContainerForCustomer extends StatelessWidget { ], ), ], - ) - .onPress(onTapped) - .toWhiteContainer(width: double.infinity, allPading: 12), + ).onPress(onTapped!).toWhiteContainer(width: double.infinity, allPading: 12), ); } } diff --git a/lib/views/user/login_with_password_page.dart b/lib/views/user/login_with_password_page.dart index 01f279c..51835a4 100644 --- a/lib/views/user/login_with_password_page.dart +++ b/lib/views/user/login_with_password_page.dart @@ -46,13 +46,12 @@ class _LoginWithPasswordState extends State { void initState() { super.initState(); scheduleMicrotask(() { + userVM = Provider.of(context, listen: false); + context.read().getAvailBio(); if (AppState().currentAppType == AppType.provider) { phoneNum = "966530896018"; password = "Amir@1234"; } - userVM = Provider.of(context, listen: false); - context.read().getAvailBio(); - getCountryList(); }); diff --git a/pubspec.yaml b/pubspec.yaml index a85049c..d03f2c0 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 From b7d3e4ab72c6a6a2ac03241bd0b4f1ca33180856 Mon Sep 17 00:00:00 2001 From: "Aamir.Muhammad" <> Date: Tue, 23 Jul 2024 16:51:07 +0300 Subject: [PATCH 2/4] appointment fix --- lib/view_models/appointments_view_model.dart | 529 ++++++------------- 1 file changed, 176 insertions(+), 353 deletions(-) diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 0cf8865..4e8d64c 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -45,10 +45,7 @@ class AppointmentsVM extends BaseVM { final BranchRepo branchRepo; final AppointmentRepo appointmentRepo; - AppointmentsVM({required this.commonServices, - required this.appointmentRepo, - required this.commonRepo, - required this.branchRepo}); + AppointmentsVM({required this.commonServices, required this.appointmentRepo, required this.commonRepo, required this.branchRepo}); bool isUpcommingEnabled = true; bool isFetchingLists = false; @@ -76,8 +73,7 @@ class AppointmentsVM extends BaseVM { List serviceAppointmentScheduleList = []; bool ifItemAlreadySelected(int id) { - int indexFound = allSelectedItemsInAppointments - .indexWhere((element) => element.id == id); + int indexFound = allSelectedItemsInAppointments.indexWhere((element) => element.id == id); if (indexFound != -1) { return true; } @@ -98,8 +94,7 @@ class AppointmentsVM extends BaseVM { Future onItemsSelectedInService() async { if (currentServiceSelection != null) { - int index = servicesInCurrentAppointment.indexWhere((element) => - element.serviceId == currentServiceSelection!.serviceId!); + int index = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!); if (index == -1) { double totalPrice = 0.0; @@ -116,11 +111,8 @@ class AppointmentsVM extends BaseVM { } Future onPayNowPressedForAppointment({required BuildContext context, required int appointmentID}) async { - context - .read() - .updateAppointmentIdsForPayment(ids: [appointmentID]); - navigateWithName(context, AppRoutes.paymentMethodsView, - arguments: PaymentTypes.partialAppointment); + context.read().updateAppointmentIdsForPayment(ids: [appointmentID]); + navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.partialAppointment); } Future onBookAppointmentPressed(BuildContext context) async { @@ -128,8 +120,7 @@ class AppointmentsVM extends BaseVM { bool isSuccess = false; List appointmentIdsList = []; try { - GenericRespModel genericRespModel = - await appointmentRepo.createServiceAppointment( + GenericRespModel genericRespModel = await appointmentRepo.createServiceAppointment( schedules: serviceAppointmentScheduleList, serviceProviderID: selectedBranchModel!.serviceProviderId ?? 0, ); @@ -155,17 +146,13 @@ class AppointmentsVM extends BaseVM { } context.read().onNavbarTapped(1); - applyFilterOnAppointmentsVM( - appointmentStatusEnum: AppointmentStatusEnum.booked); + applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.booked); Utils.hideLoading(context); resetAfterBookingAppointment(); if (isSuccess) { if (amountToPayForAppointment > 0) { - context - .read() - .updateAppointmentIdsForPayment(ids: appointmentIdsList); - navigateWithName(context, AppRoutes.paymentMethodsView, - arguments: PaymentTypes.appointment); + context.read().updateAppointmentIdsForPayment(ids: appointmentIdsList); + navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment); } else { Utils.showToast(LocaleKeys.appointmentBookedSuccessfully.tr()); getMyAppointments(); @@ -178,34 +165,27 @@ class AppointmentsVM extends BaseVM { } Future onConfirmAppointmentPressed({required BuildContext context, required appointmentId}) async { - context - .read() - .updateAppointmentIdsForPayment(ids: [appointmentId]); - navigateWithName(context, AppRoutes.paymentMethodsView, - arguments: PaymentTypes.appointment); + context.read().updateAppointmentIdsForPayment(ids: [appointmentId]); + navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment); } - Future onCancelAppointmentPressed({required BuildContext context, - required AppointmentListModel appointmentListModel}) async { + Future onCancelAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async { Utils.showLoading(context); try { - GenericRespModel genericRespModel = - await appointmentRepo.cancelOrRescheduleServiceAppointment( + GenericRespModel genericRespModel = await appointmentRepo.cancelOrRescheduleServiceAppointment( serviceAppointmentID: appointmentListModel.id ?? 0, serviceSlotID: appointmentListModel.serviceSlotID ?? 0, appointmentScheduleAction: 2, // 1 for Reschedule and 2 for Cancel ); - if (genericRespModel.messageStatus == 2 || - genericRespModel.data == null) { + if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) { Utils.hideLoading(context); Utils.showToast("${genericRespModel.message.toString()}"); return; } if (genericRespModel.messageStatus == 1) { context.read().onNavbarTapped(1); - applyFilterOnAppointmentsVM( - appointmentStatusEnum: AppointmentStatusEnum.cancelled); + applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled); Utils.showToast("${genericRespModel.message.toString()}"); await getMyAppointments(); Utils.hideLoading(context); @@ -236,8 +216,7 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - SelectionModel branchSelectedCategoryId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateProviderCategoryId(SelectionModel id) { branchSelectedCategoryId = id; @@ -252,30 +231,23 @@ class AppointmentsVM extends BaseVM { void updateBranchServiceId(SelectionModel id) async { branchSelectedServiceId = id; - currentServiceSelection = branchServices.firstWhere( - (element) => element.serviceProviderServiceId == id.selectedId); + currentServiceSelection = branchServices.firstWhere((element) => element.serviceProviderServiceId == id.selectedId); notifyListeners(); } void removeServiceInCurrentAppointment(int index) { - int serviceId = servicesInCurrentAppointment - .elementAt(index) - .serviceProviderServiceId ?? - -1; - allSelectedItemsInAppointments.removeWhere( - (element) => element.serviceProviderServiceId == serviceId); + int serviceId = servicesInCurrentAppointment.elementAt(index).serviceProviderServiceId ?? -1; + allSelectedItemsInAppointments.removeWhere((element) => element.serviceProviderServiceId == serviceId); servicesInCurrentAppointment.removeAt(index); notifyListeners(); } resetCategorySelectionBottomSheet() { selectedSubServicesCounter = 0; - branchSelectedCategoryId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); isHomeTapped = false; - branchSelectedServiceId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); currentServiceSelection = null; } @@ -290,29 +262,21 @@ class AppointmentsVM extends BaseVM { Future populateAppointmentsFilterList() async { if (appointmentsFilterOptions.isNotEmpty) return; - myAppointmentsEnum = await commonRepo.getEnumTypeValues( - enumTypeID: 13); // 13 is to get Appointments Filter Enums + myAppointmentsEnum = await commonRepo.getEnumTypeValues(enumTypeID: 13); // 13 is to get Appointments Filter Enums for (int i = 0; i < myAppointmentsEnum.length; i++) { - appointmentsFilterOptions.add(FilterListModel( - title: myAppointmentsEnum[i].enumValueStr, - isSelected: false, - id: myAppointmentsEnum[i].enumValue)); + appointmentsFilterOptions.add(FilterListModel(title: myAppointmentsEnum[i].enumValueStr, isSelected: false, id: myAppointmentsEnum[i].enumValue)); } - appointmentsFilterOptions.insert( - 0, FilterListModel(title: "All Appointments", isSelected: true, id: 0)); + appointmentsFilterOptions.insert(0, FilterListModel(title: "All Appointments", isSelected: true, id: 0)); // TODO: THIS SHOULD REMOVED AND ADDED IN THE ENUMS API - appointmentsFilterOptions.add( - FilterListModel(title: "Work In Progress", isSelected: false, id: 7)); - appointmentsFilterOptions.add( - FilterListModel(title: "Visit Completed", isSelected: false, id: 8)); + appointmentsFilterOptions.add(FilterListModel(title: "Work In Progress", isSelected: false, id: 7)); + appointmentsFilterOptions.add(FilterListModel(title: "Visit Completed", isSelected: false, id: 8)); notifyListeners(); } - applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum, - bool isNeedCustomerFilter = false}) { + applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum, bool isNeedCustomerFilter = false}) { // isNeedCustomerFilter IS ONLY FOR THE PROVIDER APP if (appointmentsFilterOptions.isEmpty) return; for (var value in appointmentsFilterOptions) { @@ -320,8 +284,7 @@ class AppointmentsVM extends BaseVM { } for (var element in appointmentsFilterOptions) { - if (element.id == - appointmentStatusEnum.getIdFromAppointmentStatusEnum()) { + if (element.id == appointmentStatusEnum.getIdFromAppointmentStatusEnum()) { element.isSelected = true; } } @@ -332,15 +295,13 @@ class AppointmentsVM extends BaseVM { if (appointmentStatusEnum.getIdFromAppointmentStatusEnum() == 0) { myFilteredAppointments = myAppointments; if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers(); + notifyListeners(); return; } - myFilteredAppointments = myAppointments - .where((element) => - element.appointmentStatusID! == - appointmentStatusEnum.getIdFromAppointmentStatusEnum()) - .toList(); + myFilteredAppointments = myAppointments.where((element) => element.appointmentStatusID! == appointmentStatusEnum.getIdFromAppointmentStatusEnum()).toList(); if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers(); + notifyListeners(); } findAppointmentsBasedOnCustomers() { @@ -354,9 +315,7 @@ class AppointmentsVM extends BaseVM { // Create a list of CustomerData instances myFilteredAppointments2 = uniqueCustomerIDs.map((id) { - List list = myFilteredAppointments - .where((item) => item.customerID == id) - .toList(); + List list = myFilteredAppointments.where((item) => item.customerID == id).toList(); AppointmentListModel model = list.first; model.customerAppointmentList = list; return model; @@ -376,35 +335,42 @@ class AppointmentsVM extends BaseVM { // }).toList(); } - Future getMyAppointments() async { - setState(ViewState.busy); + // 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(); + // } - myAppointments = - await appointmentRepo.getMyAppointmentsForCustomersByFilters(); - myFilteredAppointments = myAppointments; - myUpComingAppointments = myAppointments - .where((element) => - element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) - .toList(); + Future getMyAppointments({bool isNeedToRebuild = false}) async { + if (isNeedToRebuild) setState(ViewState.busy); - applyFilterOnAppointmentsVM( - appointmentStatusEnum: AppointmentStatusEnum.allAppointments); - if (myUpComingAppointments.isEmpty) { - isShowEmptyMessage = true; - } + myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters(); + // myFilteredAppointments = myAppointments; + myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.booked).toList(); setState(ViewState.idle); + applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments); notifyListeners(); } AppointmentSlots? appointmentSlots; - Future getAppointmentSlotsInfo({required Map map, - required BuildContext context, - bool isNeedToRebuild = false}) async { + Future getAppointmentSlotsInfo({required Map map, required BuildContext context, bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); try { - MResponse genericRespModel = - await appointmentRepo.getAppointmentSlots(map); + MResponse genericRespModel = await appointmentRepo.getAppointmentSlots(map); if (genericRespModel.messageStatus == 1) { appointmentSlots = AppointmentSlots.fromJson(genericRespModel.data); } else { @@ -415,26 +381,21 @@ class AppointmentsVM extends BaseVM { } } - Future getMyAppointmentsForProvider(Map map,) async { + Future getMyAppointmentsForProvider( + Map map, + ) async { setState(ViewState.busy); myAppointments = await appointmentRepo.getMyAppointmentsForProvider(map); myFilteredAppointments = myAppointments; - myUpComingAppointments = await myAppointments - .where((element) => - element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) - .toList(); - applyFilterOnAppointmentsVM( - appointmentStatusEnum: AppointmentStatusEnum.allAppointments, - isNeedCustomerFilter: true); + myUpComingAppointments = await myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.confirmed).toList(); + applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true); setState(ViewState.idle); } - updateAppointmentStatus(Map map, - {bool isNeedToRebuild = false}) async { + updateAppointmentStatus(Map map, {bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); try { - MResponse genericRespModel = - await appointmentRepo.updateAppointmentStatus(map); + MResponse genericRespModel = await appointmentRepo.updateAppointmentStatus(map); if (genericRespModel.messageStatus == 1) { Utils.showToast(LocaleKeys.appointmentStatusUpdated.tr()); @@ -446,12 +407,10 @@ class AppointmentsVM extends BaseVM { } } - updateAppointmentPaymentStatus(Map map, - {bool isNeedToRebuild = false}) async { + updateAppointmentPaymentStatus(Map map, {bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); try { - MResponse genericRespModel = - await appointmentRepo.updateAppointmentPaymentStatus(map); + MResponse genericRespModel = await appointmentRepo.updateAppointmentPaymentStatus(map); if (genericRespModel.messageStatus == 1) { Utils.showToast(LocaleKeys.paymentStatusUpdated.tr()); @@ -463,11 +422,9 @@ class AppointmentsVM extends BaseVM { } } - Future createMergeAppointment(Map map, - {bool isNeedToRebuild = false}) async { + Future createMergeAppointment(Map map, {bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); - MResponse genericRespModel = - await appointmentRepo.createMergeAppointment(map); + MResponse genericRespModel = await appointmentRepo.createMergeAppointment(map); return genericRespModel; } @@ -475,16 +432,10 @@ class AppointmentsVM extends BaseVM { bool inNeedToEnableMergeButton = false; updateCheckBoxInMergeRequest(int currentIndex) { - myFilteredAppointments2[selectedAppointmentIndex] - .customerAppointmentList![currentIndex] - .isSelected = !(myFilteredAppointments2[selectedAppointmentIndex] - .customerAppointmentList?[currentIndex] - .isSelected ?? - false); - - int count = countSelected(myFilteredAppointments2[selectedAppointmentIndex] - .customerAppointmentList ?? - []); + myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList![currentIndex].isSelected = + !(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false); + + int count = countSelected(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList ?? []); if (count > 1) inNeedToEnableMergeButton = true; else @@ -493,59 +444,35 @@ 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}) { - for (var element in serviceAppointmentScheduleList[scheduleIndex] - .customTimeDateSlotList!) { + for (var element in serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!) { element.date!.isSelected = false; } - serviceAppointmentScheduleList[scheduleIndex] - .customTimeDateSlotList![dateIndex] - .date! - .isSelected = true; + serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.isSelected = true; serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex = dateIndex; final date = TimeSlotModel( - date: serviceAppointmentScheduleList[scheduleIndex] - .customTimeDateSlotList![dateIndex] - .date! - .date, - slotId: serviceAppointmentScheduleList[scheduleIndex] - .customTimeDateSlotList![dateIndex] - .date! - .slotId, + date: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.date, + slotId: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.slotId, isSelected: true, slot: "", ); - serviceAppointmentScheduleList[scheduleIndex] - .selectedCustomTimeDateSlotModel = CustomTimeDateSlotModel(date: date); + serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel = CustomTimeDateSlotModel(date: date); notifyListeners(); } updateSelectedAppointmentSlotByDate({required int scheduleIndex, required int slotIndex}) { - for (var element in serviceAppointmentScheduleList[scheduleIndex] - .customTimeDateSlotList!) { + for (var element in serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!) { for (var element in element.availableSlots!) { element.isSelected = false; } } - int index = - serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!; - serviceAppointmentScheduleList[scheduleIndex] - .customTimeDateSlotList![index] - .availableSlots![slotIndex] - .isSelected = true; - serviceAppointmentScheduleList[scheduleIndex] - .selectedCustomTimeDateSlotModel! - .availableSlots = - serviceAppointmentScheduleList[scheduleIndex] - .customTimeDateSlotList![index] - .availableSlots!; + int index = serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!; + serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots![slotIndex].isSelected = true; + serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel!.availableSlots = serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots!; notifyListeners(); } @@ -559,8 +486,7 @@ class AppointmentsVM extends BaseVM { int selectedSubServicesCounter = 0; onItemUpdateOrSelected(int index, bool selected, int itemId) { - int serviceIndex = servicesInCurrentAppointment.indexWhere( - (element) => element.serviceId == currentServiceSelection!.serviceId!); + int serviceIndex = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!); // print("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}"); // if (serviceIndex == -1) { // return; @@ -575,28 +501,19 @@ class AppointmentsVM extends BaseVM { allSelectedItemsInAppointments.add(serviceItemsFromApi[index]); for (var element in allSelectedItemsInAppointments) { if (!ifItemAlreadySelected(element.id!)) { - servicesInCurrentAppointment[serviceIndex] - .serviceItems! - .add(serviceItemsFromApi[index]); + servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]); servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = - servicesInCurrentAppointment[serviceIndex] - .currentTotalServicePrice + - double.parse((serviceItemsFromApi[index].price) ?? "0.0"); + servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0"); } } } if (!selected) { selectedSubServicesCounter = selectedSubServicesCounter - 1; - currentServiceSelection!.serviceItems! - .removeWhere((element) => element.id == itemId); - allSelectedItemsInAppointments - .removeWhere((element) => element.id == itemId); + currentServiceSelection!.serviceItems!.removeWhere((element) => element.id == itemId); + allSelectedItemsInAppointments.removeWhere((element) => element.id == itemId); servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = - servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - - double.parse((serviceItemsFromApi[index].price) ?? "0.0"); - servicesInCurrentAppointment[serviceIndex] - .serviceItems! - .removeWhere((element) => element.id == itemId); + servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0"); + servicesInCurrentAppointment[serviceIndex].serviceItems!.removeWhere((element) => element.id == itemId); } notifyListeners(); } @@ -608,8 +525,7 @@ class AppointmentsVM extends BaseVM { } branchesFilterOptions[index].isSelected = true; - await getBranchesBasedOnCategoryFilters( - categoryId: branchesFilterOptions[index].id); + await getBranchesBasedOnCategoryFilters(categoryId: branchesFilterOptions[index].id); notifyListeners(); } @@ -628,8 +544,7 @@ class AppointmentsVM extends BaseVM { String pickHomeLocationError = ""; String selectSubServicesError = ""; - SelectionModel branchSelectedServiceId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updatePickHomeLocationError(String value) { pickHomeLocationError = value; @@ -688,9 +603,7 @@ class AppointmentsVM extends BaseVM { builder: (BuildContext context) { double totalKms = 15.3; return InfoBottomSheet( - title: LocaleKeys.chargesBreakdown - .tr() - .toText(fontSize: 24, isBold: true), + title: LocaleKeys.chargesBreakdown.tr().toText(fontSize: 24, isBold: true), description: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -698,49 +611,36 @@ 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( mainAxisAlignment: MainAxisAlignment.end, children: [ - "${selectedService.currentTotalServicePrice} SAR" - .toText(fontSize: 16, isBold: true), + "${selectedService.currentTotalServicePrice} SAR".toText(fontSize: 16, isBold: true), ], ), if (selectedService.isHomeSelected) ...[ 20.height, - LocaleKeys.homeLocation - .tr() - .toText(fontSize: 16, isBold: true), + LocaleKeys.homeLocation.tr().toText(fontSize: 16, isBold: true), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - "${totalKms}km ".toText( - fontSize: 12, - color: MyColors.lightTextColor, - isBold: true), - "${selectedService.rangePricePerKm} x $totalKms" - .toText(fontSize: 12, isBold: true), + "${totalKms}km ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + "${selectedService.rangePricePerKm} x $totalKms".toText(fontSize: 12, isBold: true), ], ), 8.height, Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - "${selectedService.rangePricePerKm ?? 0 * totalKms} SAR" - .toText(fontSize: 16, isBold: true), + "${selectedService.rangePricePerKm ?? 0 * totalKms} SAR".toText(fontSize: 16, isBold: true), ], ), ], @@ -748,24 +648,16 @@ class AppointmentsVM extends BaseVM { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.totalAmount - .tr() - .toText(fontSize: 16, isBold: true), + LocaleKeys.totalAmount.tr().toText(fontSize: 16, isBold: true), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ (selectedService.isHomeSelected - ? "${(selectedService.currentTotalServicePrice) + (double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms)}" - : "${selectedService.currentTotalServicePrice}") + ? "${(selectedService.currentTotalServicePrice) + (double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms)}" + : "${selectedService.currentTotalServicePrice}") .toText(fontSize: 29, isBold: true), 2.width, - LocaleKeys.sar - .tr() - .toText( - color: MyColors.lightTextColor, - fontSize: 16, - isBold: true) - .paddingOnly(bottom: 5), + LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), ], ) ], @@ -785,8 +677,7 @@ class AppointmentsVM extends BaseVM { isValidated = false; break; } - if (schedule.selectedCustomTimeDateSlotModel!.date == null || - !schedule.selectedCustomTimeDateSlotModel!.date!.isSelected) { + if (schedule.selectedCustomTimeDateSlotModel!.date == null || !schedule.selectedCustomTimeDateSlotModel!.date!.isSelected) { isValidated = false; break; } else { @@ -794,9 +685,7 @@ class AppointmentsVM extends BaseVM { isValidated = false; break; } else { - TimeSlotModel slot = schedule - .selectedCustomTimeDateSlotModel!.availableSlots! - .firstWhere((element) => element.isSelected); + TimeSlotModel slot = schedule.selectedCustomTimeDateSlotModel!.availableSlots!.firstWhere((element) => element.isSelected); if (slot.date.isNotEmpty) { isValidated = true; break; @@ -824,8 +713,7 @@ class AppointmentsVM extends BaseVM { } } - serviceAppointmentScheduleList = - await appointmentRepo.mergeServiceIntoAvailableSchedules( + serviceAppointmentScheduleList = await appointmentRepo.mergeServiceIntoAvailableSchedules( serviceItemIdsForHome: serviceItemIdsForHome, serviceItemIdsForWorkshop: serviceItemIdsForWorkshop, ); @@ -838,21 +726,17 @@ class AppointmentsVM extends BaseVM { totalAmount = 0.0; amountToPayForAppointment = 0.0; for (var schedule in serviceAppointmentScheduleList) { - amountToPayForAppointment = - amountToPayForAppointment + (schedule.amountToPay ?? 0.0); + amountToPayForAppointment = amountToPayForAppointment + (schedule.amountToPay ?? 0.0); totalAmount = totalAmount + (schedule.amountTotal ?? 0.0); } Utils.hideLoading(context); - navigateWithName(context, AppRoutes.bookAppointmenSchedulesView, - arguments: ScreenArgumentsForAppointmentDetailPage( - routeFlag: 1, appointmentId: 0)); // 1 For Creating an Appointment + navigateWithName(context, AppRoutes.bookAppointmenSchedulesView, arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 1, appointmentId: 0)); // 1 For Creating an Appointment notifyListeners(); } - Future onRescheduleAppointmentPressed({required BuildContext context, - required AppointmentListModel appointmentListModel}) async { + Future onRescheduleAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async { Utils.showLoading(context); List serviceItemIdsForHome = []; @@ -869,8 +753,7 @@ class AppointmentsVM extends BaseVM { } } - serviceAppointmentScheduleList = - await appointmentRepo.mergeServiceIntoAvailableSchedules( + serviceAppointmentScheduleList = await appointmentRepo.mergeServiceIntoAvailableSchedules( serviceItemIdsForHome: serviceItemIdsForHome, serviceItemIdsForWorkshop: serviceItemIdsForWorkshop, ); @@ -885,35 +768,29 @@ class AppointmentsVM extends BaseVM { navigateWithName( context, AppRoutes.bookAppointmenSchedulesView, - arguments: ScreenArgumentsForAppointmentDetailPage( - routeFlag: 2, appointmentId: appointmentListModel.id ?? 0), + arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 2, appointmentId: appointmentListModel.id ?? 0), ); // 2 For Rescheduling an Appointment notifyListeners(); } - Future onRescheduleAppointmentConfirmPressed({required BuildContext context, - required int appointmentId, - required int selectedSlotId}) async { + Future onRescheduleAppointmentConfirmPressed({required BuildContext context, required int appointmentId, required int selectedSlotId}) async { Utils.showLoading(context); try { - GenericRespModel genericRespModel = - await appointmentRepo.cancelOrRescheduleServiceAppointment( + GenericRespModel genericRespModel = await appointmentRepo.cancelOrRescheduleServiceAppointment( serviceAppointmentID: appointmentId, serviceSlotID: selectedSlotId, appointmentScheduleAction: 1, // 1 for Reschedule and 2 for Cancel ); - if (genericRespModel.messageStatus == 2 || - genericRespModel.data == null) { + if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) { Utils.hideLoading(context); Utils.showToast("${genericRespModel.message.toString()}"); return; } if (genericRespModel.messageStatus == 1) { context.read().onNavbarTapped(1); - applyFilterOnAppointmentsVM( - appointmentStatusEnum: AppointmentStatusEnum.cancelled); + applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled); Utils.showToast("${genericRespModel.message.toString()}"); getMyAppointments(); Utils.hideLoading(context); @@ -950,13 +827,9 @@ class AppointmentsVM extends BaseVM { setOnlyState(ViewState.busy); Category category = await branchRepo.fetchBranchCategory(); category.data?.forEach((element) { - branchesFilterOptions.add(FilterListModel( - id: element.id ?? 0, - isSelected: false, - title: element.categoryName ?? "N/A")); + branchesFilterOptions.add(FilterListModel(id: element.id ?? 0, isSelected: false, title: element.categoryName ?? "N/A")); }); - branchesFilterOptions.insert( - 0, FilterListModel(id: 0, isSelected: true, title: "All Branches")); + branchesFilterOptions.insert(0, FilterListModel(id: 0, isSelected: true, title: "All Branches")); notifyListeners(); setState(ViewState.idle); } @@ -966,10 +839,8 @@ class AppointmentsVM extends BaseVM { if (isNeedToRebuild) setState(ViewState.busy); if (isFromRefresh) { - var selectedBranch = - branchesFilterOptions.firstWhere((element) => element.isSelected); - nearbyBranches = await branchRepo - .getBranchesByFilters(categoryIdsList: [selectedBranch.id]); + var selectedBranch = branchesFilterOptions.firstWhere((element) => element.isSelected); + nearbyBranches = await branchRepo.getBranchesByFilters(categoryIdsList: [selectedBranch.id]); setState(ViewState.idle); return; } @@ -980,16 +851,14 @@ class AppointmentsVM extends BaseVM { void getBranchCategories() async { for (var value in selectedBranchModel!.branchServices!) { if (!isCategoryAlreadyPresent(value.categoryId!)) { - branchCategories - .add(DropValue(value.categoryId!, value.categoryName!, "")); + branchCategories.add(DropValue(value.categoryId!, value.categoryName!, "")); } } notifyListeners(); } getBranchServices({required int categoryId}) async { - branchSelectedServiceId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); isHomeTapped = false; pickedHomeLocation = ""; pickHomeLocationError = ""; @@ -1002,16 +871,13 @@ class AppointmentsVM extends BaseVM { } List getFilteredBranchServices({required int categoryId}) { - List filteredServices = selectedBranchModel!.branchServices! - .where((element) => element.categoryId == categoryId) - .toList(); + List filteredServices = selectedBranchModel!.branchServices!.where((element) => element.categoryId == categoryId).toList(); return filteredServices; } getBranchAndServices(int providerId) async { providerProfileModel = null; - providerProfileModel = - await branchRepo.getBranchAndServicesByProviderId(providerId); + providerProfileModel = await branchRepo.getBranchAndServicesByProviderId(providerId); setState(ViewState.idle); } @@ -1068,16 +934,12 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - SelectionModel branchFilterSelectedProviderId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel branchFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - void updateBranchFilterSelectedProviderId(SelectionModel id, - {bool isForSearch = false}) async { + void updateBranchFilterSelectedProviderId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { - DropValue providerDrop = providersDropList - .firstWhere((element) => element.id == id.selectedId); - if (!ifAlreadyExist( - list: branchFilterProviderSearchHistory, value: providerDrop)) { + DropValue providerDrop = providersDropList.firstWhere((element) => element.id == id.selectedId); + if (!ifAlreadyExist(list: branchFilterProviderSearchHistory, value: providerDrop)) { addBranchFilterProviderSearchHistory(value: providerDrop); } } @@ -1111,16 +973,12 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - SelectionModel branchFilterSelectedCategoryId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel branchFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - void updateBranchFilterSelectedCategoryId(SelectionModel id, - {bool isForSearch = false}) async { + void updateBranchFilterSelectedCategoryId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { - DropValue categoryDrop = - categoryDropList.firstWhere((element) => element.id == id.selectedId); - if (!ifAlreadyExist( - list: branchFilterCategorySearchHistory, value: categoryDrop)) { + DropValue categoryDrop = categoryDropList.firstWhere((element) => element.id == id.selectedId); + if (!ifAlreadyExist(list: branchFilterCategorySearchHistory, value: categoryDrop)) { addBranchFilterCategorySearchHistory(value: categoryDrop); } } @@ -1154,16 +1012,12 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - SelectionModel branchFilterSelectedServiceId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel branchFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - void updateBranchFilterSelectedServiceId(SelectionModel id, - {bool isForSearch = false}) async { + void updateBranchFilterSelectedServiceId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { - DropValue serviceDrop = - servicesDropList.firstWhere((element) => element.id == id.selectedId); - if (!ifAlreadyExist( - list: branchFilterServicesSearchHistory, value: serviceDrop)) { + DropValue serviceDrop = servicesDropList.firstWhere((element) => element.id == id.selectedId); + if (!ifAlreadyExist(list: branchFilterServicesSearchHistory, value: serviceDrop)) { addBranchFilterServicesSearchHistory(value: serviceDrop); } notifyListeners(); @@ -1204,8 +1058,7 @@ class AppointmentsVM extends BaseVM { providersDropList.clear(); setOnlyState(ViewState.busy); - List providers = - await branchRepo.getAllProvidersWitheBasicData(); + List providers = await branchRepo.getAllProvidersWitheBasicData(); for (var element in providers) { providersDropList.add( DropValue(element.id ?? 0, element.providerName ?? "N/A", ""), @@ -1224,10 +1077,10 @@ class AppointmentsVM 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"), ""), ); @@ -1239,8 +1092,7 @@ class AppointmentsVM extends BaseVM { if (servicesDropList.isNotEmpty) return; servicesDropList.clear(); setState(ViewState.busy); - Services services = await branchRepo.fetchServicesByCategoryId( - serviceCategoryId: -1); // to get all services + Services services = await branchRepo.fetchServicesByCategoryId(serviceCategoryId: -1); // to get all services for (var element in services.data!) { servicesDropList.add( @@ -1273,12 +1125,9 @@ class AppointmentsVM extends BaseVM { } void clearBranchFilterSelections() { - branchFilterSelectedProviderId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - branchFilterSelectedCategoryId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - branchFilterSelectedServiceId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + branchFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + branchFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + branchFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); } Future getBranchesBasedOnFilters() async { @@ -1322,8 +1171,7 @@ class AppointmentsVM extends BaseVM { } setState(ViewState.busy); nearbyBranches.clear(); - nearbyBranches = - await branchRepo.getBranchesByFilters(categoryIdsList: [categoryId]); + nearbyBranches = await branchRepo.getBranchesByFilters(categoryIdsList: [categoryId]); setState(ViewState.idle); } @@ -1334,11 +1182,9 @@ class AppointmentsVM extends BaseVM { Future fetchAllBranchesBySelectedProviderId({required List providersIdsList}) async { branchesDropList.clear(); setOnlyState(ViewState.busy); - List providers = await branchRepo.getBranchesByFilters( - providerIdsList: providersIdsList); + List providers = await branchRepo.getBranchesByFilters(providerIdsList: providersIdsList); for (var element in providers) { - branchesDropList - .add(DropValue(element.id ?? 0, element.branchName ?? "N/A", "")); + branchesDropList.add(DropValue(element.id ?? 0, element.branchName ?? "N/A", "")); } setState(ViewState.idle); @@ -1373,8 +1219,7 @@ class AppointmentsVM extends BaseVM { for (var element in appointmentFilterProviderSearchHistory) { providerIdsSelected.add(element.id); } - fetchAllBranchesBySelectedProviderId( - providersIdsList: providerIdsSelected); + fetchAllBranchesBySelectedProviderId(providersIdsList: providerIdsSelected); } if (appointmentFilterProviderSearchHistory.isEmpty) { @@ -1397,16 +1242,12 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - SelectionModel appointmentFilterSelectedProviderId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel appointmentFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - void updateAppointmentFilterSelectedProviderId(SelectionModel id, - {bool isForSearch = false}) async { + void updateAppointmentFilterSelectedProviderId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { - DropValue providerDrop = providersDropList - .firstWhere((element) => element.id == id.selectedId); - if (!ifAlreadyExist( - list: appointmentFilterProviderSearchHistory, value: providerDrop)) { + DropValue providerDrop = providersDropList.firstWhere((element) => element.id == id.selectedId); + if (!ifAlreadyExist(list: appointmentFilterProviderSearchHistory, value: providerDrop)) { addAppointmentFilterProviderSearchHistory(value: providerDrop); } } @@ -1440,16 +1281,12 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - SelectionModel appointmentFilterSelectedBranchId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel appointmentFilterSelectedBranchId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - void updateAppointmentFilterSelectedBranchId(SelectionModel id, - {bool isForSearch = false}) async { + void updateAppointmentFilterSelectedBranchId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { - DropValue branchesDrop = - branchesDropList.firstWhere((element) => element.id == id.selectedId); - if (!ifAlreadyExist( - list: appointmentFilterBranchSearchHistory, value: branchesDrop)) { + DropValue branchesDrop = branchesDropList.firstWhere((element) => element.id == id.selectedId); + if (!ifAlreadyExist(list: appointmentFilterBranchSearchHistory, value: branchesDrop)) { addAppointmentFilterBranchSearchHistory(value: branchesDrop); } } @@ -1483,16 +1320,12 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - SelectionModel appointmentFilterSelectedCategoryId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel appointmentFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - void updateAppointmentFilterSelectedCategoryId(SelectionModel id, - {bool isForSearch = false}) async { + void updateAppointmentFilterSelectedCategoryId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { - DropValue categoryDrop = - categoryDropList.firstWhere((element) => element.id == id.selectedId); - if (!ifAlreadyExist( - list: appointmentFilterCategorySearchHistory, value: categoryDrop)) { + DropValue categoryDrop = categoryDropList.firstWhere((element) => element.id == id.selectedId); + if (!ifAlreadyExist(list: appointmentFilterCategorySearchHistory, value: categoryDrop)) { addAppointmentFilterCategorySearchHistory(value: categoryDrop); } } @@ -1524,16 +1357,12 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - SelectionModel appointmentFilterSelectedServiceId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - void updateAppointmentFilterSelectedServiceId(SelectionModel id, - {bool isForSearch = false}) async { + void updateAppointmentFilterSelectedServiceId(SelectionModel id, {bool isForSearch = false}) async { if (isForSearch) { - DropValue servicesDrop = - servicesDropList.firstWhere((element) => element.id == id.selectedId); - if (!ifAlreadyExist( - list: appointmentFilterServicesSearchHistory, value: servicesDrop)) { + DropValue servicesDrop = servicesDropList.firstWhere((element) => element.id == id.selectedId); + if (!ifAlreadyExist(list: appointmentFilterServicesSearchHistory, value: servicesDrop)) { addAppointmentFilterServicesSearchHistory(value: servicesDrop); } } @@ -1542,14 +1371,10 @@ class AppointmentsVM extends BaseVM { } void clearAppointmentFilterSelections() { - appointmentFilterSelectedProviderId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - appointmentFilterSelectedCategoryId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - appointmentFilterSelectedServiceId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - appointmentFilterSelectedBranchId = - SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + appointmentFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + appointmentFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + appointmentFilterSelectedBranchId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); } void clearAppointmentFilters() { @@ -1590,15 +1415,13 @@ class AppointmentsVM extends BaseVM { } } - myAppointments = - await appointmentRepo.getMyAppointmentsForCustomersByFilters( + myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters( providerIdsList: providersIdsList.isNotEmpty ? providersIdsList : null, categoryIdsList: categoryIdsList.isNotEmpty ? categoryIdsList : null, serviceIdsList: servicesIdsList.isNotEmpty ? servicesIdsList : null, branchIdsList: branchesIdsList.isNotEmpty ? branchesIdsList : null, ); - applyFilterOnAppointmentsVM( - appointmentStatusEnum: AppointmentStatusEnum.allAppointments); + applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments); setState(ViewState.idle); } } From cb04d4a2fb429c27ffa3fe44e2882b177e92393c Mon Sep 17 00:00:00 2001 From: "Aamir.Muhammad" <> Date: Wed, 24 Jul 2024 09:25:24 +0300 Subject: [PATCH 3/4] arabic fonts, logout, subscription app state. --- assets/fonts/GESS-Two-Bold.otf | Bin 0 -> 20880 bytes assets/fonts/GESS-Two-Light.otf | Bin 0 -> 19932 bytes assets/fonts/GESS-Two-Medium.otf | Bin 0 -> 20412 bytes assets/langs/ar-SA.json | 3 +- assets/langs/en-US.json | 3 +- lib/classes/app_state.dart | 9 ++ lib/classes/consts.dart | 1 + lib/generated/codegen_loader.g.dart | 6 +- lib/generated/locale_keys.g.dart | 1 + lib/theme/app_theme.dart | 6 +- lib/view_models/appointments_view_model.dart | 2 +- lib/view_models/subscriptions_view_model.dart | 1 + lib/view_models/user_view_model.dart | 34 +++++-- .../appointments/review_appointment_view.dart | 2 +- .../customer_appointment_slider_widget.dart | 93 +++++++++--------- .../setting_options_language.dart | 64 +++++------- pubspec.yaml | 7 +- 17 files changed, 126 insertions(+), 106 deletions(-) create mode 100644 assets/fonts/GESS-Two-Bold.otf create mode 100644 assets/fonts/GESS-Two-Light.otf create mode 100644 assets/fonts/GESS-Two-Medium.otf diff --git a/assets/fonts/GESS-Two-Bold.otf b/assets/fonts/GESS-Two-Bold.otf new file mode 100644 index 0000000000000000000000000000000000000000..da99274a456a9297ec68fc6f8476bf3701f80383 GIT binary patch literal 20880 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/GESS-Two-Light.otf b/assets/fonts/GESS-Two-Light.otf new file mode 100644 index 0000000000000000000000000000000000000000..60ea874e0f21bb3e7fb2e8dcd796760590a57171 GIT binary patch literal 19932 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 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..6a2a963 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -291,4 +291,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 8f4e664..d893157 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -582,7 +582,8 @@ class CodegenLoader extends AssetLoader{ "defineLicenses": "تحديد التراخيص", "logOut": "تسجيل الخروج", "customer": "العميل", - "accept": "قبول" + "accept": "قبول", + "amountVAR": "كمية" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1153,7 +1154,8 @@ static const Map en_US = { "defineLicenses": "Define Licenses", "logOut": "Log Out", "customer": "Customer", - "accept": "Accept" + "accept": "Accept", + "amountVAR": "Amount" }; 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 355108c..e854db3 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -546,5 +546,6 @@ abstract class LocaleKeys { static const logOut = 'logOut'; static const customer = 'customer'; static const accept = 'accept'; + static const amountVAR = 'amountVAR'; } 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 4e8d64c..e14683c 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -359,7 +359,7 @@ class AppointmentsVM extends BaseVM { myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters(); // myFilteredAppointments = myAppointments; - myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.booked).toList(); + myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed ).toList(); setState(ViewState.idle); applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments); notifyListeners(); diff --git a/lib/view_models/subscriptions_view_model.dart b/lib/view_models/subscriptions_view_model.dart index 546ed13..5f1ac44 100644 --- a/lib/view_models/subscriptions_view_model.dart +++ b/lib/view_models/subscriptions_view_model.dart @@ -178,6 +178,7 @@ class SubscriptionsVM extends BaseVM { 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 34b65f2..dcc2351 100644 --- a/lib/view_models/user_view_model.dart +++ b/lib/view_models/user_view_model.dart @@ -163,7 +163,8 @@ class UserVM extends BaseVM { } } - Future performCompleteProfile(BuildContext context, { + Future performCompleteProfile( + BuildContext context, { required String password, required String confirmPassword, required String firstName, @@ -350,7 +351,7 @@ 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!); if (user.messageStatus == 1) { Response response2 = await userRepo.loginV2OTPVerify(user.data!.userToken ?? "", "9999"); @@ -377,17 +378,17 @@ class UserVM extends BaseVM { SharedPrefManager.setData(jsonEncode(user.data!.userInfo!.toJson())); navigateReplaceWithName(context, AppRoutes.dashboard); } else { - Utils.showToast(LocaleKeys.onlyCustomerApp.tr()); + Utils.showToast(LocaleKeys.onlyCustomerApp.tr()); } } } else { - Utils.showToast(verifiedUser.message ?? ""); + Utils.showToast(verifiedUser.message ?? ""); } } } 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 { @@ -518,8 +519,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)); @@ -527,8 +528,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 { @@ -639,4 +640,17 @@ 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) { + navigateReplaceWithNameUntilRoute(context, AppRoutes.loginWithPassword); + } } 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 5eb7be0..4374a5a 100644 --- a/lib/views/appointments/widgets/customer_appointment_slider_widget.dart +++ b/lib/views/appointments/widgets/customer_appointment_slider_widget.dart @@ -24,24 +24,23 @@ class CustomerAppointmentSliderWidget extends StatelessWidget { Widget getCorouselWidget(AppType appType, AppointmentsVM appointmentsVM) { return CarouselSlider.builder( options: CarouselOptions( - height: 110, + height: appType == AppType.provider ? 110 : 140, viewportFraction: 1.0, enlargeCenterPage: false, enableInfiniteScroll: false, ), itemCount: appointmentsVM.myUpComingAppointments.length, - itemBuilder: (BuildContext context, int itemIndex, int pageViewIndex) => - BuildAppointmentContainerForCustomer( - isForHome: true, - appointmentListModel: appointmentsVM.myUpComingAppointments[itemIndex], - onTapped: () { - if (appType == AppType.provider) { - onAppointmentClick!(appointmentsVM.myUpComingAppointments[itemIndex]); - } else { - navigateWithName(context, AppRoutes.appointmentDetailView, arguments: appointmentsVM.myUpComingAppointments[itemIndex]); - } - }, - ), + 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]); + } + }, + ), ); } @@ -179,7 +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) { @@ -205,17 +204,17 @@ 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( @@ -237,31 +236,31 @@ 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, - ), - ], - ), ], ), ), diff --git a/lib/views/setting_options/setting_options_language.dart b/lib/views/setting_options/setting_options_language.dart index 69a5bea..282ab5d 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( @@ -114,8 +108,7 @@ class SettingOptionsLanguage extends StatelessWidget { 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/pubspec.yaml b/pubspec.yaml index d03f2c0..4e176a9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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 From b4a3819c367899a6bc87bd4215220cff3eec0d46 Mon Sep 17 00:00:00 2001 From: "Aamir.Muhammad" <> Date: Mon, 29 Jul 2024 15:11:05 +0300 Subject: [PATCH 4/4] subscription & profile --- assets/langs/ar-SA.json | 6 +- assets/langs/en-US.json | 6 +- lib/classes/consts.dart | 1 + lib/generated/codegen_loader.g.dart | 12 +-- lib/generated/locale_keys.g.dart | 4 +- .../provider_subscription_model.dart | 86 +++++++++++-------- .../subscription_model.dart | 28 +++--- lib/repositories/user_repo.dart | 15 +++- lib/view_models/subscriptions_view_model.dart | 11 ++- lib/view_models/user_view_model.dart | 24 +++--- .../ad_review_containers.dart | 2 +- .../advertisement/select_ad_type_view.dart | 2 +- .../provider_license_page.dart | 33 +++---- .../setting_options_language.dart | 4 +- lib/views/user/login_with_password_page.dart | 6 +- lib/widgets/common_widgets/app_bar.dart | 36 ++++---- lib/widgets/tab/menu_tabs.dart | 27 ++---- lib/widgets/tab/role_type_tab.dart | 9 +- 18 files changed, 163 insertions(+), 149 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 65ecac1..d0857a2 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,7 +561,7 @@ "inviteFriends": "دعوة الأصدقاء", "more": "المزيد", "language": "اللغة", - "mySubscriptions": "اشتراكاتي", + "mySubscription": "اشتراكاتي", "subscriptions": "الاشتراكات", "defineLicenses": "تحديد التراخيص", "logOut": "تسجيل الخروج", diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index a3f396e..9010e64 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", @@ -492,7 +492,7 @@ "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", @@ -562,7 +562,7 @@ "inviteFriends": "Invite Friends", "more": "More", "language" :"Language", - "mySubscriptions": "My Subscriptions", + "mySubscriptions": "My Subscription", "subscriptions": "Subscriptions", "defineLicenses": "Define Licenses", "logOut": "Log Out", diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 6a2a963..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"; diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index d893157..22be496 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,7 +577,7 @@ class CodegenLoader extends AssetLoader{ "inviteFriends": "دعوة الأصدقاء", "more": "المزيد", "language": "اللغة", - "mySubscriptions": "اشتراكاتي", + "mySubscription": "اشتراكاتي", "subscriptions": "الاشتراكات", "defineLicenses": "تحديد التراخيص", "logOut": "تسجيل الخروج", @@ -663,7 +663,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 +1079,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,7 +1149,7 @@ 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", diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index e854db3..f02cbb4 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,7 +540,7 @@ 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'; diff --git a/lib/models/subscriptions_models/provider_subscription_model.dart b/lib/models/subscriptions_models/provider_subscription_model.dart index 46a5c40..9cb2ae7 100644 --- a/lib/models/subscriptions_models/provider_subscription_model.dart +++ b/lib/models/subscriptions_models/provider_subscription_model.dart @@ -18,6 +18,9 @@ class ProviderSubscriptionModel { bool? isUpgradeLater; bool? isTrialSubscription; dynamic currentSubscription; + int? totalBranches; + int? totalSubUsers; + int? totalAds; ProviderSubscriptionModel({ this.id, @@ -36,7 +39,10 @@ class ProviderSubscriptionModel { this.isUpgradeLater, this.isTrialSubscription, this.currentSubscription, - this.subscriptionID + this.subscriptionID, + this.totalAds, + this.totalBranches, + this.totalSubUsers, }); factory ProviderSubscriptionModel.fromRawJson(String str) => ProviderSubscriptionModel.fromJson(json.decode(str)); @@ -44,42 +50,48 @@ class ProviderSubscriptionModel { 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"], - ); + 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, - }; + "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/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/view_models/subscriptions_view_model.dart b/lib/view_models/subscriptions_view_model.dart index 5f1ac44..f0ff88f 100644 --- a/lib/view_models/subscriptions_view_model.dart +++ b/lib/view_models/subscriptions_view_model.dart @@ -35,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); @@ -56,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 { @@ -156,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; + } } } @@ -165,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); diff --git a/lib/view_models/user_view_model.dart b/lib/view_models/user_view_model.dart index dcc2351..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'; @@ -650,7 +643,18 @@ class UserVM extends BaseVM { notifyListeners(); } - void logout(BuildContext context) { - navigateReplaceWithNameUntilRoute(context, AppRoutes.loginWithPassword); + 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/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 282ab5d..22bf940 100644 --- a/lib/views/setting_options/setting_options_language.dart +++ b/lib/views/setting_options/setting_options_language.dart @@ -103,8 +103,8 @@ 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: () { diff --git a/lib/views/user/login_with_password_page.dart b/lib/views/user/login_with_password_page.dart index 51835a4..c6f1bb3 100644 --- a/lib/views/user/login_with_password_page.dart +++ b/lib/views/user/login_with_password_page.dart @@ -49,8 +49,10 @@ class _LoginWithPasswordState extends State { userVM = Provider.of(context, listen: false); context.read().getAvailBio(); if (AppState().currentAppType == AppType.provider) { - phoneNum = "966530896018"; - password = "Amir@1234"; + // 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 ), + ), ), ),