import 'package:flutter/material.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/models/general_models/enums_model.dart'; 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/provider_branches_models/provider_profile_model.dart'; import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart'; import 'package:mc_common_app/models/services_models/item_model.dart'; import 'package:mc_common_app/models/services_models/service_model.dart'; import 'package:mc_common_app/models/general_models/widgets_models.dart'; import 'package:mc_common_app/repositories/appointment_repo.dart'; import 'package:mc_common_app/repositories/common_repo.dart'; import 'package:mc_common_app/repositories/provider_repo.dart'; import 'package:mc_common_app/services/common_services.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/utils/utils.dart'; import 'package:mc_common_app/view_models/base_view_model.dart'; import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/view_models/payment_view_model.dart'; import 'package:mc_common_app/views/appointments/book_appointment_schedules_view.dart'; import 'package:mc_common_app/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart'; import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; import '../models/appointments_models/appointment_slots.dart'; class AppointmentsVM extends BaseVM { final CommonRepo commonRepo; final CommonAppServices commonServices; final ProviderRepo providerRepo; final AppointmentRepo scheduleRepo; AppointmentsVM({required this.commonServices, required this.scheduleRepo, required this.providerRepo, required this.commonRepo}); bool isFetchingLists = false; int selectedBranch = 0; int selectedAppointmentIndex = 0; int selectedAppointmentSubIndex = 0; int selectedAppointmentId = 0; List myAppointments = []; List myUpComingAppointments = []; List myFilteredAppointments = []; List appointmentsFilterOptions = []; List customersAppointments = []; List myFilteredAppointments2 = []; // List availableSchedules = []; bool isFetchingServices = false; List branchCategories = []; bool isHomeTapped = false; List serviceAppointmentScheduleList = []; bool ifItemAlreadySelected(int id) { int indexFound = allSelectedItemsInAppointments .indexWhere((element) => element.id == id); if (indexFound != -1) { return true; } return false; } List allSelectedItemsInAppointments = []; setupProviderAppointmentFilter() { appointmentsFilterOptions.clear(); appointmentsFilterOptions.add( FilterListModel(id: 0, title: "All Appointments", isSelected: true)); appointmentsFilterOptions .add(FilterListModel(id: 2, title: "Confirmed", isSelected: false)); appointmentsFilterOptions .add(FilterListModel(id: 3, title: "Arrived", isSelected: false)); appointmentsFilterOptions .add( FilterListModel(id: 7, title: "Work In Progress", isSelected: false)); appointmentsFilterOptions .add(FilterListModel(id: 8, title: "Completed", isSelected: false)); appointmentsFilterOptions .add(FilterListModel(id: 4, title: "Canceled", isSelected: false)); } Future onItemsSelectedInService() async { if (currentServiceSelection != null) { int index = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!); if (index == -1) { double totalPrice = 0.0; for (var element in currentServiceSelection!.serviceItems!) { totalPrice = totalPrice + double.parse(element.price ?? "0.0"); } currentServiceSelection!.currentTotalServicePrice = totalPrice; servicesInCurrentAppointment.insert(0, currentServiceSelection!); } resetCategorySelectionBottomSheet(); notifyListeners(); } } Future onBookAppointmentPressed(BuildContext context) async { Utils.showLoading(context); bool isSuccess = false; List appointmentIdsList = []; try { GenericRespModel genericRespModel = await scheduleRepo.createServiceAppointment( schedules: serviceAppointmentScheduleList, serviceProviderID: selectedBranchModel!.serviceProviderId ?? 0, ); if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) { Utils.hideLoading(context); Utils.showToast("${genericRespModel.message.toString()}"); return; } if (genericRespModel.data != null) { genericRespModel.data.forEach((element) { if (element['appointmentID'] != 0) { appointmentIdsList.add(element['appointmentID']); isSuccess = true; } else { isSuccess = false; return; } }); } context.read().onNavbarTapped(1); 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); } else { Utils.showToast("Your appointment has been booked successfully!"); getMyAppointments(); navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); } } } catch (e) { Utils.showToast("${e.toString()}"); } } Future onConfirmAppointmentPressed( {required BuildContext context, required appointmentId}) async { context .read() .updateAppointmentIdsForPayment(ids: [appointmentId]); navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment); } Future onCancelAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async { Utils.showLoading(context); try { GenericRespModel genericRespModel = await scheduleRepo.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) { Utils.hideLoading(context); Utils.showToast("${genericRespModel.message.toString()}"); return; } if (genericRespModel.data == 1) { context.read().onNavbarTapped(1); applyFilterOnAppointmentsVM( appointmentStatusEnum: AppointmentStatusEnum.cancelled); Utils.showToast("${genericRespModel.message.toString()}"); await getMyAppointments(); Utils.hideLoading(context); getMyAppointments(); navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); } } catch (e) { Utils.showToast("${e.toString()}"); } } void updateIsHomeTapped(bool value) { isHomeTapped = value; if (currentServiceSelection != null) { currentServiceSelection!.isHomeSelected = value; } notifyListeners(); } String pickedHomeLocation = ""; void updatePickedHomeLocation(String value) { pickedHomeLocation = value; pickHomeLocationError = ""; if (currentServiceSelection != null) { currentServiceSelection!.homeLocation = value; } notifyListeners(); } SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateProviderCategoryId(SelectionModel id) { branchSelectedCategoryId = id; getBranchServices(categoryId: branchSelectedCategoryId.selectedId); notifyListeners(); } List providersFilterOptions = []; List nearbyBranches = []; BranchDetailModel? selectedBranchModel; List branchServices = []; List servicesInCurrentAppointment = []; ServiceModel? currentServiceSelection; void updateBranchServiceId(SelectionModel id) async { branchSelectedServiceId = id; 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); servicesInCurrentAppointment.removeAt(index); notifyListeners(); } resetCategorySelectionBottomSheet() { selectedSubServicesCounter = 0; branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); isHomeTapped = false; branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); currentServiceSelection = null; } resetAfterBookingAppointment() { allSelectedItemsInAppointments.clear(); // servicesInCurrentAppointment.clear(); serviceAppointmentScheduleList.clear(); } List myAppointmentsEnum = []; populateAppointmentsFilterList() async { appointmentsFilterOptions.clear(); myAppointmentsEnum = await commonRepo.getEnumTypeValues( enumTypeID: 13); //TODO: 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.insert( 0, FilterListModel(title: "All Appointments", isSelected: true, id: 0)); notifyListeners(); } applyFilterOnAppointmentsVM( {required AppointmentStatusEnum appointmentStatusEnum, bool isNeedCustomerFilter = false}) { if (appointmentsFilterOptions.isEmpty) return; for (var value in appointmentsFilterOptions) { value.isSelected = false; } appointmentsFilterOptions.forEach((element) { if (element.id == appointmentStatusEnum.getIdFromAppointmentStatusEnum()) { element.isSelected = true; } }); // appointmentsFilterOptions[ // appointmentStatusEnum.getIdFromAppointmentStatusEnum()] // .isSelected = true; if (appointmentStatusEnum.getIdFromAppointmentStatusEnum() == 0) { myFilteredAppointments = myAppointments; if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers(); notifyListeners(); return; } myFilteredAppointments = myAppointments .where((element) => element.appointmentStatusID! == appointmentStatusEnum.getIdFromAppointmentStatusEnum()) .toList(); if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers(); notifyListeners(); } findAppointmentsBasedOnCustomers() { // Use a Set to ensure uniqueness of customerIDs Set uniqueCustomerIDs = Set(); // Extract unique customerIDs for (var item in myFilteredAppointments) { uniqueCustomerIDs.add(item.customerID ?? 0); } // Create a list of CustomerData instances myFilteredAppointments2 = uniqueCustomerIDs.map((id) { List list = myFilteredAppointments .where((item) => item.customerID == id) .toList(); AppointmentListModel model = list.first; model.customerAppointmentList = list; return model; }).toList(); // customersAppointments = uniqueCustomerIDs.map((id) { // List list = myFilteredAppointments // .where((item) => item.customerID == id) // .toList(); // var customerItem = // myFilteredAppointments.firstWhere((item) => item.customerID == id); // // return CustomerData( // customerID: id, // customerName: customerItem.customerName ?? "", // appointmentList: list, // ); // }).toList(); } Future getMyAppointments({bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); myAppointments = await commonRepo.getMyAppointments(); 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 { if (isNeedToRebuild) setState(ViewState.busy); try { MResponse genericRespModel = await scheduleRepo.getAppointmentSlots(map); if (genericRespModel.messageStatus == 1) { appointmentSlots = AppointmentSlots.fromJson(genericRespModel.data); } else { Utils.showToast(genericRespModel.message.toString()); } } catch (e) { Utils.showToast(e.toString()); } } Future getProviderMyAppointments(Map map, {bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); myAppointments = await scheduleRepo.getMyAppointments(map); myFilteredAppointments = myAppointments; myUpComingAppointments = myAppointments .where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.booked) .toList(); applyFilterOnAppointmentsVM( appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true); setState(ViewState.idle); } updateSelectedBranch(BranchDetailModel branchDetailModel) { selectedBranchModel = branchDetailModel; getBranchCategories(); notifyListeners(); } updateAppointmentStatus(Map map, {bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); try { MResponse genericRespModel = await scheduleRepo.updateAppointmentStatus(map); if (genericRespModel.messageStatus == 1) { Utils.showToast("appointment status updated"); } else { Utils.showToast(genericRespModel.message.toString()); } } catch (e) { Utils.showToast(e.toString()); } } updateAppointmentPaymentStatus(Map map, {bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); try { MResponse genericRespModel = await scheduleRepo.updateAppointmentPaymentStatus(map); if (genericRespModel.messageStatus == 1) { Utils.showToast("payment status updated"); } else { Utils.showToast(genericRespModel.message.toString()); } } catch (e) { Utils.showToast(e.toString()); } } Future createMergeAppointment(Map map, {bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); MResponse genericRespModel = await scheduleRepo.createMergeAppointment(map); return genericRespModel; } bool inNeedToEnableMergeButton = false; updateCheckBoxInMergeRequest(int currentIndex) { myFilteredAppointments2[selectedAppointmentIndex] .customerAppointmentList![currentIndex] .isSelected = !(myFilteredAppointments2[selectedAppointmentIndex] .customerAppointmentList?[currentIndex] .isSelected ?? false); int count = countSelected(myFilteredAppointments2[selectedAppointmentIndex] .customerAppointmentList ?? []); if (count > 1) inNeedToEnableMergeButton = true; else inNeedToEnableMergeButton = false; notifyListeners(); } int countSelected(List appointments) { return appointments .where((appointment) => appointment.isSelected == true) .toList() .length; } updateSelectedAppointmentDate( {required int dateIndex, required int scheduleIndex}) { for (var element in serviceAppointmentScheduleList[scheduleIndex] .customTimeDateSlotList!) { element.date!.isSelected = false; } 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, isSelected: true, slot: "", ); 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 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!; notifyListeners(); } double amountToPayForAppointment = 0.0; double totalAmount = 0.0; List serviceItemsFromApi = []; ProviderProfileModel? providerProfileModel; int selectedSubServicesCounter = 0; onItemUpdateOrSelected(int index, bool selected, int itemId) { int serviceIndex = servicesInCurrentAppointment.indexWhere( (element) => element.serviceId == currentServiceSelection!.serviceId!); // print("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}"); // if (serviceIndex == -1) { // return; // } serviceItemsFromApi[index].isUpdateOrSelected = selected; serviceItemsFromApi[index].isHomeSelected = isHomeTapped; if (selected) { selectedSubServicesCounter = selectedSubServicesCounter + 1; selectSubServicesError = ""; currentServiceSelection!.serviceItems!.add(serviceItemsFromApi[index]); allSelectedItemsInAppointments.add(serviceItemsFromApi[index]); for (var element in allSelectedItemsInAppointments) { if (!ifItemAlreadySelected(element.id!)) { servicesInCurrentAppointment[serviceIndex] .serviceItems! .add(serviceItemsFromApi[index]); servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = 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); servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0"); servicesInCurrentAppointment[serviceIndex] .serviceItems! .removeWhere((element) => element.id == itemId); } notifyListeners(); } populateBranchesFilterList() { providersFilterOptions.clear(); providersFilterOptions = [ FilterListModel(title: "All Providers", isSelected: true, id: 0), FilterListModel(title: "Maintenance", isSelected: false, id: 1), FilterListModel(title: "Oil Service", isSelected: false, id: 2), FilterListModel(title: "Accessories", isSelected: false, id: 3), FilterListModel(title: "Tire Service", isSelected: false, id: 4), FilterListModel(title: "Dent and Paint", isSelected: false, id: 5), ]; notifyListeners(); } applyFilterOnProviders({required int index}) { if (providersFilterOptions.isEmpty) return; for (var value in providersFilterOptions) { value.isSelected = false; } providersFilterOptions[index].isSelected = true; notifyListeners(); } getAllNearBranches({bool isNeedToRebuild = false}) async { //TODO: needs to lat,long into API nearbyBranches.clear(); if (isNeedToRebuild) setState(ViewState.busy); nearbyBranches = await providerRepo.getAllNearBranchAndServices(); setState(ViewState.idle); } Future> getServiceItems(int serviceId) async { serviceItemsFromApi.clear(); serviceItemsFromApi = await providerRepo.getServiceItems(serviceId); for (var item in serviceItemsFromApi) { if (ifItemAlreadySelected(item.id!)) { item.isUpdateOrSelected = true; } } setState(ViewState.idle); return serviceItemsFromApi; } getBranchAndServices(int providerId) async { providerProfileModel = null; providerProfileModel = await providerRepo.getBranchAndServices(providerId); setState(ViewState.idle); } String pickHomeLocationError = ""; String selectSubServicesError = ""; SelectionModel branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); bool isCategoryAlreadyPresent(int id) { final contain = branchCategories.where((element) => element.id == id); if (contain.isEmpty) { return false; } return true; } void getBranchCategories() async { for (var value in selectedBranchModel!.branchServices!) { if (!isCategoryAlreadyPresent(value.categoryId!)) { branchCategories .add(DropValue(value.categoryId!, value.categoryName!, "")); } } notifyListeners(); } getBranchServices({required int categoryId}) async { branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); isHomeTapped = false; pickedHomeLocation = ""; pickHomeLocationError = ""; if (categoryId != -1) { isFetchingServices = true; branchServices = getFilteredBranchServices(categoryId: categoryId); isFetchingServices = false; notifyListeners(); } } List getFilteredBranchServices({required int categoryId}) { List filteredServices = selectedBranchModel!.branchServices! .where((element) => element.categoryId == categoryId) .toList(); return filteredServices; } void updatePickHomeLocationError(String value) { pickHomeLocationError = value; // notifyListeners(); } bool isServiceSelectionValidated() { if (branchSelectedServiceId.selectedId == -1) { return false; } if (isHomeTapped) { if (pickedHomeLocation == "") { updatePickHomeLocationError(GlobalConsts.homeLocationEmptyError); return false; } } return true; } bool validateItemsSelection() { for (var value in serviceItemsFromApi) { if (value.isUpdateOrSelected!) { return true; } } selectSubServicesError = "Please select at least one sub service"; notifyListeners(); return false; } String getTotalPrice(List serviceItems) { var totalPrice = 0.0; for (var element in serviceItems) { totalPrice = totalPrice + (element.currentTotalServicePrice); } return totalPrice.toString(); } void openTheAddServiceBottomSheet(BuildContext context, AppointmentsVM appointmentsVM) { showModalBottomSheet( context: context, isScrollControlled: true, enableDrag: true, builder: (BuildContext context) { return AppointmentServicePickBottomSheet(); }, ); } void priceBreakDownClicked(BuildContext context, ServiceModel selectedService) { showModalBottomSheet( context: context, isScrollControlled: true, enableDrag: true, builder: (BuildContext context) { double totalKms = 15.3; return InfoBottomSheet( title: "Charges Breakdown".toText(fontSize: 24, isBold: true), description: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "Services".toText(fontSize: 16, isBold: true), 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), ], ), ), ), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ "${selectedService.currentTotalServicePrice} SAR" .toText(fontSize: 16, isBold: true), ], ), if (selectedService.isHomeSelected) ...[ 20.height, "Home Location".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), ], ), 8.height, Row( mainAxisAlignment: MainAxisAlignment.end, children: [ "${selectedService.rangePricePerKm ?? 0 * totalKms} SAR" .toText(fontSize: 16, isBold: true), ], ), ], 30.height, Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "Total Amount ".toText(fontSize: 16, isBold: true), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ (selectedService.isHomeSelected ? "${(selectedService.currentTotalServicePrice) + (double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms)}" : "${selectedService.currentTotalServicePrice}") .toText(fontSize: 29, isBold: true), 2.width, "SAR" .toText( color: MyColors.lightTextColor, fontSize: 16, isBold: true) .paddingOnly(bottom: 5), ], ) ], ), 30.height, ], )); }); } void onReviewButtonPressed(BuildContext context) { bool isValidated = false; for (int i = 0; i < serviceAppointmentScheduleList.length; i++) { final schedule = serviceAppointmentScheduleList[i]; if (schedule.selectedCustomTimeDateSlotModel == null) { isValidated = false; break; } if (schedule.selectedCustomTimeDateSlotModel!.date == null || !schedule.selectedCustomTimeDateSlotModel!.date!.isSelected) { isValidated = false; break; } else { if (schedule.selectedCustomTimeDateSlotModel!.availableSlots == null) { isValidated = false; break; } else { TimeSlotModel slot = schedule .selectedCustomTimeDateSlotModel!.availableSlots! .firstWhere((element) => element.isSelected); if (slot.date.isNotEmpty) { isValidated = true; break; } } } } if (!isValidated) { Utils.showToast( "You must select appointment time for each schedule's appointment."); return; } navigateWithName(context, AppRoutes.reviewAppointmentView); } void onServicesNextPressed(BuildContext context) async { Utils.showLoading(context); List serviceItemIdsForHome = []; List serviceItemIdsForWorkshop = []; for (var serviceItem in allSelectedItemsInAppointments) { if (serviceItem.isHomeSelected!) { serviceItemIdsForHome.add(serviceItem.id!.toString()); } else { serviceItemIdsForWorkshop.add(serviceItem.id!.toString()); } } serviceAppointmentScheduleList = await scheduleRepo.mergeServiceIntoAvailableSchedules( serviceItemIdsForHome: serviceItemIdsForHome, serviceItemIdsForWorkshop: serviceItemIdsForWorkshop, ); if (serviceAppointmentScheduleList.isEmpty) { Utils.hideLoading(context); Utils.showToast( "There are no available appointments for selected Items."); return; } totalAmount = 0.0; amountToPayForAppointment = 0.0; for (var schedule in serviceAppointmentScheduleList) { 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 notifyListeners(); } Future onRescheduleAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async { Utils.showLoading(context); List serviceItemIdsForHome = []; List serviceItemIdsForWorkshop = []; for (var service in appointmentListModel.appointmentServicesList!) { for (var serviceItem in service.serviceItems!) { serviceItemIdsForWorkshop.add(serviceItem.id!.toString()); // if (serviceItem.isHomeSelected ?? false) { // serviceItemIdsForHome.add(serviceItem.id!.toString()); // } else { // serviceItemIdsForWorkshop.add(serviceItem.id!.toString()); // } } } serviceAppointmentScheduleList = await scheduleRepo.mergeServiceIntoAvailableSchedules( serviceItemIdsForHome: serviceItemIdsForHome, serviceItemIdsForWorkshop: serviceItemIdsForWorkshop, ); if (serviceAppointmentScheduleList.isEmpty) { Utils.hideLoading(context); Utils.showToast( "There are no available appointments for selected Items."); return; } Utils.hideLoading(context); navigateWithName( context, AppRoutes.bookAppointmenSchedulesView, 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 { Utils.showLoading(context); try { GenericRespModel genericRespModel = await scheduleRepo.cancelOrRescheduleServiceAppointment( serviceAppointmentID: appointmentId, serviceSlotID: selectedSlotId, appointmentScheduleAction: 1, // 1 for Reschedule and 2 for Cancel ); if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) { Utils.hideLoading(context); Utils.showToast("${genericRespModel.message.toString()}"); return; } if (genericRespModel.data == 1) { context.read().onNavbarTapped(1); applyFilterOnAppointmentsVM( appointmentStatusEnum: AppointmentStatusEnum.cancelled); Utils.showToast("${genericRespModel.message.toString()}"); getMyAppointments(); Utils.hideLoading(context); navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); } } catch (e) { Utils.showToast("${e.toString()}"); } } }