From 533952e14e4c9438abaa662c292001d509faddb2 Mon Sep 17 00:00:00 2001 From: FaizHashmiCS22 Date: Tue, 18 Jul 2023 16:47:00 +0300 Subject: [PATCH] Services merging in Schedules (In Progress) --- lib/config/customer_dependencies.dart | 2 + lib/config/customer_routes.dart | 4 + lib/main.dart | 3 +- lib/repositories/provider_repo.dart | 6 +- lib/repositories/schedule_repo.dart | 70 ++++++++++ lib/view_models/appointments_view_model.dart | 125 ++++++++++++----- .../book_appointment_services_view.dart | 130 +++++++++++------- .../book_appointments_item_view.dart | 83 +++++++---- ...appointment_service_pick_bottom_sheet.dart | 19 ++- .../fragments/branches_fragment.dart | 3 +- lib/views/provider/branch_detail_page.dart | 11 +- lib/views/provider/provider_profile_page.dart | 2 +- .../provider/sheet/items_list_sheet.dart | 4 +- 13 files changed, 337 insertions(+), 125 deletions(-) create mode 100644 lib/repositories/schedule_repo.dart diff --git a/lib/config/customer_dependencies.dart b/lib/config/customer_dependencies.dart index 3debdff..804a53f 100644 --- a/lib/config/customer_dependencies.dart +++ b/lib/config/customer_dependencies.dart @@ -1,10 +1,12 @@ import 'package:car_customer_app/repositories/provider_repo.dart'; +import 'package:car_customer_app/repositories/schedule_repo.dart'; import 'package:mc_common_app/config/dependencies.dart'; class CustomerDependencies { static void addDependencies() { AppDependencies.addDependencies(); injector.registerSingleton(() => ProviderRepoImp()); + injector.registerSingleton(() => ScheduleRepoImp()); } } diff --git a/lib/config/customer_routes.dart b/lib/config/customer_routes.dart index 7c9b98a..6c1851e 100644 --- a/lib/config/customer_routes.dart +++ b/lib/config/customer_routes.dart @@ -1,6 +1,8 @@ import 'package:car_customer_app/views/appointments/appointment_detail_view.dart'; import 'package:car_customer_app/views/appointments/book_appointment_services_view.dart'; +import 'package:car_customer_app/views/appointments/book_appointments_item_view.dart'; import 'package:car_customer_app/views/appointments/pick_items_view.dart'; +import 'package:car_customer_app/views/appointments/review_appointment_view.dart'; import 'package:car_customer_app/views/dashboard/dashboard_page.dart'; import 'package:car_customer_app/views/provider/branch_detail_page.dart'; import 'package:car_customer_app/views/provider/provider_profile_page.dart'; @@ -21,6 +23,8 @@ class CustomerAppRoutes { AppRoutes.adsDetailView: (context) => AdsDetailView(adDetails: ModalRoute.of(context)!.settings.arguments as AdDetailsModel), AppRoutes.createAdView: (context) => CreateAdView(), AppRoutes.bookAppointmenServicesView: (context) => BookAppointmentServicesView(), + AppRoutes.bookAppointmentsItemView: (context) => BookAppointmentsItemView(), + AppRoutes.reviewAppointmentView: (context) => ReviewAppointment(), AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(), AppRoutes.branchDetailPage: (context) => BranchDetailPage(branchDetailModel: ModalRoute.of(context)!.settings.arguments as BranchDetailModel), AppRoutes.providerProfilePage: (context) => ProviderProfilePage(providerId: ModalRoute.of(context)!.settings.arguments as int), diff --git a/lib/main.dart b/lib/main.dart index a2cfeff..966f089 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:car_customer_app/config/customer_dependencies.dart'; import 'package:car_customer_app/config/customer_routes.dart'; import 'package:car_customer_app/repositories/provider_repo.dart'; +import 'package:car_customer_app/repositories/schedule_repo.dart'; import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:car_customer_app/view_models/dashboard_view_model.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -26,7 +27,6 @@ import 'package:provider/provider.dart'; import 'package:provider/single_child_widget.dart'; import 'package:sizer/sizer.dart'; - //test commit Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -56,6 +56,7 @@ Future main() async { ), ChangeNotifierProvider( create: (_) => AppointmentsVM( + scheduleRepo: injector.get(), providerRepo: injector.get(), commonServices: injector.get(), commonRepo: injector.get(), diff --git a/lib/repositories/provider_repo.dart b/lib/repositories/provider_repo.dart index 702a764..fee2cc1 100644 --- a/lib/repositories/provider_repo.dart +++ b/lib/repositories/provider_repo.dart @@ -12,7 +12,7 @@ import 'package:mc_common_app/models/services/item_model.dart'; abstract class ProviderRepo { Future> getAllNearBranchAndServices(); - Future> getServiceItems(int serviceId); + Future> getServiceItems(int serviceId); Future getBranchAndServices(int providerId); } @@ -29,7 +29,7 @@ class ProviderRepoImp implements ProviderRepo { } @override - Future> getServiceItems(int serviceId) async { + Future> getServiceItems(int serviceId) async { var queryParameters = { "ServiceProviderServiceID": serviceId.toString(), }; @@ -40,7 +40,7 @@ class ProviderRepoImp implements ProviderRepo { token: appState.getUser.data!.accessToken, queryParameters: queryParameters, ); - List serviceItems = List.generate(adsGenericModel.data.length, (index) => ServiceItemModel.fromJson(adsGenericModel.data[index])); + List serviceItems = List.generate(adsGenericModel.data.length, (index) => ItemData.fromJson(adsGenericModel.data[index])); return serviceItems; } diff --git a/lib/repositories/schedule_repo.dart b/lib/repositories/schedule_repo.dart new file mode 100644 index 0000000..7edfe49 --- /dev/null +++ b/lib/repositories/schedule_repo.dart @@ -0,0 +1,70 @@ +import 'package:mc_common_app/api/api_client.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/config/dependencies.dart'; +import 'package:mc_common_app/models/generic_resp_model.dart'; +import 'package:mc_common_app/models/m_response.dart'; +import 'package:mc_common_app/models/provider_branches_models/profile/services.dart'; +import 'package:mc_common_app/models/schedule_model.dart'; + +abstract class ScheduleRepo { + Future getAllServices(String branchId); + + Future createSchedule(Map map); + + Future addServicesInSchedule(Map map); + + Future updateSchedule(Map map); + + Future> getSchedules(String branchId); + + Future updateServicesInSchedule(Map map); +} + +class ScheduleRepoImp implements ScheduleRepo { + @override + Future getAllServices(String branchId) async { + Map map = {"ProviderBranchID": branchId}; + String t = AppState().getUser.data!.accessToken ?? ""; + return await injector.get().getJsonForObject((json) => Services.fromJson(json), ApiConsts.getServicesOfBranch, token: t, queryParameters: map); + } + + @override + Future createSchedule(Map map) async { + String t = AppState().getUser.data!.accessToken ?? ""; + return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.createSchedule, map, token: t); + } + + @override + Future addServicesInSchedule(Map map) async { + String t = AppState().getUser.data!.accessToken ?? ""; + return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.createGroup, map, token: t); + } + + @override + Future> getSchedules(String branchId) async { + Map map = {"ServiceProviderBranchID": branchId}; + String t = AppState().getUser.data!.accessToken ?? ""; + + GenericRespModel adsGenericModel = await injector.get().getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.getSchedule, + token: t, + queryParameters: map, + ); + + return List.generate(adsGenericModel.data.length, (index) => ScheduleData.fromJson(adsGenericModel.data[index])); + } + + @override + Future updateSchedule(Map map) async { + String t = AppState().getUser.data!.accessToken ?? ""; + return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateSchedule, map, token: t); + } + + @override + Future updateServicesInSchedule(Map map) async { + String t = AppState().getUser.data!.accessToken ?? ""; + return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateGroup, map, token: t); + } +} diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index a65a99c..f3beb92 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -1,8 +1,12 @@ +import 'dart:developer'; + import 'package:car_customer_app/repositories/provider_repo.dart'; +import 'package:car_customer_app/repositories/schedule_repo.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.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/schedule_model.dart'; import 'package:mc_common_app/models/services/item_model.dart'; import 'package:mc_common_app/models/services/service_model.dart'; import 'package:mc_common_app/models/widgets_models.dart'; @@ -16,20 +20,29 @@ class AppointmentsVM extends BaseVM { final CommonRepo commonRepo; final CommonAppServices commonServices; final ProviderRepo providerRepo; + final ScheduleRepo scheduleRepo; - AppointmentsVM({required this.commonServices, required this.providerRepo, required this.commonRepo}); + AppointmentsVM({required this.commonServices, required this.scheduleRepo, required this.providerRepo, required this.commonRepo}); bool isFetchingLists = false; List myAppointments = []; List appointmentsFilterOptions = []; + List availableSchedules = []; + bool isFetchingServices = false; List branchCategories = []; bool isHomeTapped = false; + Future getSchedulesByBranchId() async { + availableSchedules = await scheduleRepo.getSchedules(selectedBranchModel!.id!.toString()); + log("schedules: ${availableSchedules.toString()}"); + notifyListeners(); + } + void updateIsHomeTapped(bool value) { isHomeTapped = value; notifyListeners(); @@ -39,38 +52,38 @@ class AppointmentsVM extends BaseVM { void updatePickedHomeLocation(String value) { pickedHomeLocation = value; + pickHomeLocationError = ""; notifyListeners(); } SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - void updateProviderCategoryId(SelectionModel id) async { + void updateProviderCategoryId(SelectionModel id) { branchSelectedCategoryId = id; - await getProviderServices(id.selectedId); + getBranchServices(categoryId: branchSelectedCategoryId.selectedId); notifyListeners(); } - List branchServices = []; + List providersFilterOptions = []; + List nearbyBranches = []; + BranchDetailModel? selectedBranchModel; - SelectionModel branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + List branchServices = []; + ServiceModel? currentServiceSelection; void updateBranchServiceId(SelectionModel id) async { branchSelectedServiceId = id; + currentServiceSelection = branchServices.firstWhere((element) => element.serviceProviderServiceId == id.selectedId); + notifyListeners(); } - getProviderServices(int categoryId) async { - branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + resetCategorySelectionBottomSheet() { + selectedSubServicesCounter = 0; + branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); isHomeTapped = false; - pickedHomeLocation = ""; - if (categoryId != -1) { - isFetchingServices = true; - notifyListeners(); - // branchServices = await commonRepo.getProviderServices(categoryId: categoryId); - isFetchingServices = false; - - notifyListeners(); - } + branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + currentServiceSelection = null; } populateAppointmentsFilterList() { @@ -101,11 +114,39 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - List providersFilterOptions = []; - List nearbyBranches = []; - List serviceItems = []; + updateSelectedBranch(BranchDetailModel branchDetailModel) { + selectedBranchModel = branchDetailModel; + getBranchCategories(); + notifyListeners(); + } + + List serviceItems = []; + List selectedServiceItems = []; ProviderProfileModel? providerProfileModel; + int selectedSubServicesCounter = 0; + + updateSelectedSubServicesCounter(int value) { + selectedSubServicesCounter = value; + notifyListeners(); + } + + onItemUpdateOrSelected(int index, bool selected, int itemId) { + serviceItems[index].isUpdateOrSelected = selected; + if (selected) { + selectedSubServicesCounter = selectedSubServicesCounter + 1; + updateSelectedSubServicesCounter(selectedSubServicesCounter); + selectSubServicesError = ""; + currentServiceSelection!.serviceItems!.add(serviceItems[index]); + } + if (!selected) { + selectedSubServicesCounter = selectedSubServicesCounter - 1; + updateSelectedSubServicesCounter(selectedSubServicesCounter); + currentServiceSelection!.serviceItems!.removeWhere((element) => element.id == itemId); + } + notifyListeners(); + } + populateProvidersFilterList() { providersFilterOptions.clear(); providersFilterOptions = [ @@ -128,7 +169,6 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - //Create new branch getAllNearBranches({bool isNeedToRebuild = false}) async { //TODO: needs to lat,long into API nearbyBranches.clear(); @@ -137,7 +177,7 @@ class AppointmentsVM extends BaseVM { setState(ViewState.idle); } - Future> getServiceItems(int serviceId) async { + Future> getServiceItems(int serviceId) async { serviceItems.clear(); serviceItems = await providerRepo.getServiceItems(serviceId); setState(ViewState.idle); @@ -151,8 +191,9 @@ class AppointmentsVM extends BaseVM { } String pickHomeLocationError = ""; + String selectSubServicesError = ""; - SelectionModel branchServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); bool isCategoryAlreadyPresent(int id) { final contain = branchCategories.where((element) => element.id == id); @@ -163,7 +204,7 @@ class AppointmentsVM extends BaseVM { } void getBranchCategories() async { - for (var value in branchServices) { + for (var value in selectedBranchModel!.branchServices!) { if (!isCategoryAlreadyPresent(value.categoryId!)) { branchCategories.add(DropValue(value.categoryId!, value.categoryName!, "")); } @@ -171,24 +212,24 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - getBranchServices(int categoryId) async { - branchServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + getBranchServices({required int categoryId}) async { + branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); isHomeTapped = false; pickedHomeLocation = ""; pickHomeLocationError = ""; if (categoryId != -1) { isFetchingServices = true; - notifyListeners(); - branchServices = getFilteredBranchServices(categoryId: categoryId, branchId: 6); + // notifyListeners(); + branchServices = getFilteredBranchServices(categoryId: categoryId); isFetchingServices = false; - notifyListeners(); } } - List getFilteredBranchServices({required int branchId, required int categoryId}) { - // List filteredServices = nearbyBranchesList.where((element) => element) - return []; + List getFilteredBranchServices({required int categoryId}) { + List filteredServices = selectedBranchModel!.branchServices!.where((element) => element.categoryId == categoryId).toList(); + log("returning: ${filteredServices.toString()}"); + return filteredServices; } void updatePickHomeLocationError(String value) { @@ -197,7 +238,7 @@ class AppointmentsVM extends BaseVM { } bool isServiceSelectionValidated() { - if (branchServiceId.selectedId == -1) { + if (branchSelectedServiceId.selectedId == -1) { return false; } @@ -209,4 +250,24 @@ class AppointmentsVM extends BaseVM { } return true; } + + bool onSubServicesNextPressed() { + for (var value in serviceItems) { + if (value.isUpdateOrSelected!) { + return true; + } + } + selectSubServicesError = "Please select at least one sub service"; + notifyListeners(); + return false; + } + + void mergeServiceInAvailableSchedule() { + for (var schedule in availableSchedules) { + for (var service in schedule.scheduleServices!) { + if (branchSelectedServiceId.selectedId == service.serviceId) {} + schedule.selectedServices!.add(currentServiceSelection!); + } + } + } } diff --git a/lib/views/appointments/book_appointment_services_view.dart b/lib/views/appointments/book_appointment_services_view.dart index ed6f6f0..186c40f 100644 --- a/lib/views/appointments/book_appointment_services_view.dart +++ b/lib/views/appointments/book_appointment_services_view.dart @@ -5,6 +5,9 @@ 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/schedule_model.dart'; +import 'package:mc_common_app/models/services/item_model.dart'; +import 'package:mc_common_app/models/services/service_model.dart'; import 'package:mc_common_app/models/widgets_models.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/navigator.dart'; @@ -148,15 +151,16 @@ class BookAppointmentServicesView extends StatelessWidget { 10.height, ListView.builder( shrinkWrap: true, - itemCount: 20, - itemBuilder: (BuildContext context, int index) { + itemCount: appointmentsVM.availableSchedules.where((element) => element.selectedServices!.isNotEmpty).toList().length, + itemBuilder: (BuildContext context, int scheduleIndex) { + ScheduleData scheduleData = appointmentsVM.availableSchedules.where((element) => element.selectedServices!.isNotEmpty).toList()[scheduleIndex]; return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: "Mechanic".toText(fontSize: 16, isBold: true), + child: "Schedule: ${scheduleData.scheduleName}".toText(fontSize: 20, isBold: true), ), Align( alignment: Alignment.topRight, @@ -164,60 +168,84 @@ class BookAppointmentServicesView extends StatelessWidget { ).onPress(() {}), ], ), - Builder(builder: (context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, + if (true) ...[ + 5.height, + Row( + mainAxisAlignment: MainAxisAlignment.start, children: [ - if (true) ...[ - Row( - crossAxisAlignment: CrossAxisAlignment.start, + ("Available Slots").toText(fontSize: 14, isBold: true), + ], + ), + 5.height, + SizedBox( + width: double.infinity, + child: BuildTimeSlots( + timeSlots: _dummySlots, + onPressed: (index) => null, + ), + ), + ], + 20.height, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.separated( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: scheduleData.selectedServices!.length, + itemBuilder: (BuildContext context, int serviceIndex) { + ServiceModel selectedService = scheduleData.selectedServices![serviceIndex]; + return Column( children: [ - "Service Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - ("Home").toText(fontSize: 12, isBold: true).expand(), - ], - ), - 8.height, - Column( - children: List.generate( - 5, - (index) => Row( - crossAxisAlignment: CrossAxisAlignment.start, + Row( children: [ - "Gear Kit: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - ("200 SAR").toText(fontSize: 12, isBold: true).expand(), + Expanded( + child: "${selectedService.serviceDescription}".toText(fontSize: 15, isBold: true), + ), ], ), - ), - ), - 8.height, - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - "120".toText(fontSize: 29, isBold: true), - 2.width, - "SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), - Icon( - Icons.arrow_drop_down, - size: 30, - ) + if (true) ...[ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Service Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + ("Home").toText(fontSize: 12, isBold: true).expand(), + ], + ), + 8.height, + Column( + children: List.generate(scheduleData.selectedServices![serviceIndex].serviceItems!.length, (itemIndex) { + ItemData itemData = selectedService.serviceItems![itemIndex]; + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "${itemData.name}: ".toText(fontSize: 13, color: MyColors.lightTextColor, isBold: true), + ("${itemData.price}").toText(fontSize: 13, isBold: true).expand(), + ], + ); + }), + ), + 8.height, + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + "120".toText(fontSize: 29, isBold: true), + 2.width, + "SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), + Icon( + Icons.arrow_drop_down, + size: 30, + ) + ], + ).onPress(() => priceBreakDownClicked(context)), + ], ], - ).onPress(() => priceBreakDownClicked(context)), - ], - if (true) ...[ - 8.height, - ("Available Slots").toText(fontSize: 14, isBold: true), - 5.height, - SizedBox( - width: double.infinity, - child: BuildTimeSlots( - timeSlots: _dummySlots, - onPressed: (index) => null, - ), - ), - ], - ], - ); - }), + ); + }, + separatorBuilder: (BuildContext context, int index) => Divider(thickness: 2), + ) + ], + ), ], ).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 10)); }, diff --git a/lib/views/appointments/book_appointments_item_view.dart b/lib/views/appointments/book_appointments_item_view.dart index cacb5c7..d375465 100644 --- a/lib/views/appointments/book_appointments_item_view.dart +++ b/lib/views/appointments/book_appointments_item_view.dart @@ -4,7 +4,10 @@ import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.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/services/item_model.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; @@ -33,7 +36,7 @@ class BookAppointmentsItemView extends StatelessWidget { width: double.infinity, color: MyColors.darkTextColor, alignment: Alignment.centerLeft, - child: "3 Items Selected".toText(fontSize: 16, color: MyColors.white).horPaddingMain(), + child: "${appointmentsVM.selectedSubServicesCounter} Item(s) Selected".toText(fontSize: 16, color: MyColors.white).horPaddingMain(), ), 16.height, Column( @@ -53,33 +56,46 @@ class BookAppointmentsItemView extends StatelessWidget { Divider(), ], ).horPaddingMain(), - ListView.separated( - separatorBuilder: (BuildContext context, int index) => Divider(), - itemCount: 20, - itemBuilder: (BuildContext context, int index) { - return ServiceItemWithPriceCheckBox( - description: "Some description about the sub-services", - title: "Engine Check", - isSelected: true, - onSelection: (bool value) {}, - priceWidget: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - // TODO: This Price will be decided according to the service selected - 150.toString().toText(fontSize: 30, isBold: true), - "SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), - ], - ), - ); - }, - ).expand(), + appointmentsVM.serviceItems.isEmpty + ? Expanded(child: Center(child: "No Items to show.".toText(fontSize: 16, color: MyColors.lightTextColor))) + : ListView.separated( + separatorBuilder: (BuildContext context, int index) => Divider(), + itemCount: appointmentsVM.serviceItems.length, + itemBuilder: (BuildContext context, int index) { + ItemData itemData = appointmentsVM.serviceItems[index]; + return ServiceItemWithPriceCheckBox( + description: "Some description about the sub-services", + title: itemData.name!, + isSelected: itemData.isUpdateOrSelected!, + onSelection: (bool value) { + appointmentsVM.onItemUpdateOrSelected(index, !itemData.isUpdateOrSelected!, itemData!.id!); + }, + priceWidget: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + // TODO: This Price will be decided according to the service selected + itemData.price!.split(".").first.toText(fontSize: 30, isBold: true), + " SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), + ], + ), + ); + }, + ).expand(), Column( children: [ Divider( height: 1, thickness: 0.7, ), - 16.height, + 8.height, + if (appointmentsVM.selectSubServicesError != "") + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + appointmentsVM.selectSubServicesError.toText(fontSize: 14, color: Colors.red), + ], + ).paddingOnly(right: 10), + 8.height, Row( children: [ Expanded( @@ -87,7 +103,10 @@ class BookAppointmentsItemView extends StatelessWidget { txtColor: MyColors.black, maxHeight: 55, title: "Cancel", - onPressed: () {}, + onPressed: () { + appointmentsVM.resetCategorySelectionBottomSheet(); + pop(context); + }, backgroundColor: MyColors.greyButtonColor, ), ), @@ -96,8 +115,22 @@ class BookAppointmentsItemView extends StatelessWidget { child: ShowFillButton( maxHeight: 55, title: "Next", - onPressed: () {}, - backgroundColor: MyColors.darkPrimaryColor, + onPressed: () async { + bool resp = appointmentsVM.onSubServicesNextPressed(); + if (resp) { + Utils.showLoading(context); + if (appointmentsVM.availableSchedules.isEmpty) { + await appointmentsVM.getSchedulesByBranchId(); + } + appointmentsVM.mergeServiceInAvailableSchedule(); + Utils.hideLoading(context); + pop(context); + pop(context); + appointmentsVM.resetCategorySelectionBottomSheet(); + + } + }, + backgroundColor: !appointmentsVM.isServiceSelectionValidated() ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.primaryColor, ), ), ], diff --git a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart index 63a2cb9..4bab6e6 100644 --- a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart +++ b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart @@ -62,19 +62,27 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { builder: (context) { List serviceCategories = []; for (var element in appointmentsVM.branchServices) { - serviceCategories.add(DropValue(element.serviceProviderServiceId ?? 0, element.serviceProviderServiceId.toString(), "")); + if (element.categoryId == appointmentsVM.branchSelectedCategoryId.selectedId) { + serviceCategories.add(DropValue( + element.serviceProviderServiceId ?? 0, + element.serviceDescription!, + "", + )); + } } + return DropdownField( (DropValue value) => appointmentsVM.updateBranchServiceId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), list: serviceCategories, - hint: "Select Services", - dropdownValue: - appointmentsVM.branchServiceId.selectedId != -1 ? DropValue(appointmentsVM.branchServiceId.selectedId, appointmentsVM.branchServiceId.selectedOption, "") : null, + hint: "Select Service", + dropdownValue: appointmentsVM.branchSelectedServiceId.selectedId != -1 + ? DropValue(appointmentsVM.branchSelectedServiceId.selectedId, appointmentsVM.branchSelectedServiceId.selectedOption, "") + : null, ); }, ), ], - if (appointmentsVM.branchServiceId.selectedId != -1 && !appointmentsVM.isFetchingServices) ...[ + if (appointmentsVM.branchSelectedServiceId.selectedId != -1 && !appointmentsVM.isFetchingServices) ...[ 16.height, Row( children: [ @@ -171,6 +179,7 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { onPressed: () { bool isValidated = appointmentsVM.isServiceSelectionValidated(); if (isValidated) { + appointmentsVM.getServiceItems(appointmentsVM.branchSelectedServiceId.selectedId); navigateWithName(context, AppRoutes.bookAppointmentsItemView); } }, diff --git a/lib/views/dashboard/fragments/branches_fragment.dart b/lib/views/dashboard/fragments/branches_fragment.dart index bca8f61..792202f 100644 --- a/lib/views/dashboard/fragments/branches_fragment.dart +++ b/lib/views/dashboard/fragments/branches_fragment.dart @@ -53,6 +53,7 @@ class BranchesFragment extends StatelessWidget { itemCount: model.nearbyBranches.length, itemBuilder: (context, index) { BranchDetailModel branchDetailModel = model.nearbyBranches[index]; + return ProviderDetailsCard( onCardTapped: () { navigateWithName(context, AppRoutes.branchDetailPage, arguments: branchDetailModel); @@ -62,7 +63,7 @@ class BranchesFragment extends StatelessWidget { providerLocation: branchDetailModel.distanceKm.toString() + " KM", providerName: branchDetailModel.serviceProviderName ?? "", providerRatings: "4.9", - items: branchDetailModel.branchServices, + services: branchDetailModel.branchServices, ); }, separatorBuilder: (context, index) { diff --git a/lib/views/provider/branch_detail_page.dart b/lib/views/provider/branch_detail_page.dart index 654e6e1..4849fe8 100644 --- a/lib/views/provider/branch_detail_page.dart +++ b/lib/views/provider/branch_detail_page.dart @@ -1,3 +1,4 @@ +import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:car_customer_app/views/provider/sheet/items_list_sheet.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -13,6 +14,7 @@ import 'package:mc_common_app/widgets/bottom_sheet.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:provider/provider.dart'; class BranchDetailPage extends StatefulWidget { final BranchDetailModel branchDetailModel; @@ -28,7 +30,7 @@ class _BranchDetailPageState extends State { void initState() { // TODO: implement initState super.initState(); - if (widget.branchDetailModel.branchServices!.length > 0) widget.branchDetailModel.branchServices?.first.isExpanded = true; + if (widget.branchDetailModel.branchServices!.length > 0) widget.branchDetailModel.branchServices?.first.isExpandedOrSelected = true; } @override @@ -151,6 +153,7 @@ class _BranchDetailPageState extends State { margin: EdgeInsets.all(21), onPressed: () { navigateWithName(context, AppRoutes.bookAppointmenServicesView); + context.read().updateSelectedBranch(widget.branchDetailModel); }, ).toContainer( paddingAll: 0, @@ -192,13 +195,13 @@ class _BranchDetailPageState extends State { ], onExpansionChanged: (value) { setState(() { - widget.branchDetailModel.branchServices![index].isExpanded = value; + widget.branchDetailModel.branchServices![index].isExpandedOrSelected = value; }); }, backgroundColor: Colors.transparent, collapsedBackgroundColor: Colors.transparent, - initiallyExpanded: widget.branchDetailModel.branchServices![index].isExpanded, - trailing: widget.branchDetailModel.branchServices![index].isExpanded ? Icon(Icons.keyboard_arrow_up) : Icon(Icons.keyboard_arrow_down), + initiallyExpanded: widget.branchDetailModel.branchServices![index].isExpandedOrSelected, + trailing: widget.branchDetailModel.branchServices![index].isExpandedOrSelected ? Icon(Icons.keyboard_arrow_up) : Icon(Icons.keyboard_arrow_down), ); }, separatorBuilder: (context, index) { diff --git a/lib/views/provider/provider_profile_page.dart b/lib/views/provider/provider_profile_page.dart index 8b411ac..3071156 100644 --- a/lib/views/provider/provider_profile_page.dart +++ b/lib/views/provider/provider_profile_page.dart @@ -92,7 +92,7 @@ class _ProviderProfilePageState extends State { providerLocation: model.providerProfileModel!.serviceProviderBranch![index].distanceKm.toString() + " KM", providerName: model.providerProfileModel!.serviceProviderBranch![index].serviceProviderName ?? "", providerRatings: "4.9", - items: model.providerProfileModel!.serviceProviderBranch![index].branchServices, + services: model.providerProfileModel!.serviceProviderBranch![index].branchServices, ); }, separatorBuilder: (context, index) { diff --git a/lib/views/provider/sheet/items_list_sheet.dart b/lib/views/provider/sheet/items_list_sheet.dart index 25cb5f4..4d09e11 100644 --- a/lib/views/provider/sheet/items_list_sheet.dart +++ b/lib/views/provider/sheet/items_list_sheet.dart @@ -35,7 +35,7 @@ class _ItemsListSheetState extends State { : ListView.separated( itemCount: appointmentsVM.serviceItems.length, itemBuilder: (BuildContext context, int index) { - ServiceItemModel serviceItemModel = appointmentsVM.serviceItems[index]; + ItemData serviceItemModel = appointmentsVM.serviceItems[index]; return SizedBox( width: double.infinity, child: Row( @@ -47,7 +47,7 @@ class _ItemsListSheetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - serviceItemModel.name.toString().toText(fontSize: 16, isBold: true), + serviceItemModel.toString().toText(fontSize: 16, isBold: true), 4.height, showItem("Available for appointment:", (serviceItemModel.isAllowAppointment ?? false) ? "Yes" : "No", valueColor: Colors.green), showItem("Allowing Workshop service:", (serviceItemModel.isAppointmentCompanyLoc ?? false) ? "Yes" : "No", valueColor: Colors.green),