From 50baaacb74dbf1edac06b5ae3603b2e110737c8b Mon Sep 17 00:00:00 2001 From: FaizHashmiCS22 Date: Wed, 14 Jun 2023 14:40:50 +0300 Subject: [PATCH 01/12] Design in Progress For Appointment Module. --- lib/config/customer_dependencies.dart | 1 - lib/config/customer_routes.dart | 6 +- lib/view_models/appointments_view_model.dart | 23 ++ .../appointment_services_list_view.dart | 155 +++++++++ .../book_appointment_services_view.dart | 325 ------------------ .../book_appointments_item_view.dart | 113 ++++++ ...appointment_service_pick_bottom_sheet.dart | 194 +++++++++++ .../service_item_with_price_checkbox.dart | 51 +++ 8 files changed, 540 insertions(+), 328 deletions(-) create mode 100644 lib/views/appointments/appointment_services_list_view.dart delete mode 100644 lib/views/appointments/book_appointment_services_view.dart create mode 100644 lib/views/appointments/book_appointments_item_view.dart create mode 100644 lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart create mode 100644 lib/views/appointments/widgets/service_item_with_price_checkbox.dart diff --git a/lib/config/customer_dependencies.dart b/lib/config/customer_dependencies.dart index 53792ef..5d7ec56 100644 --- a/lib/config/customer_dependencies.dart +++ b/lib/config/customer_dependencies.dart @@ -3,6 +3,5 @@ import 'package:mc_common_app/config/dependencies.dart'; class CustomerDependencies { static void addDependencies() { AppDependencies.addDependencies(); - } } diff --git a/lib/config/customer_routes.dart b/lib/config/customer_routes.dart index 3345264..bcf7484 100644 --- a/lib/config/customer_routes.dart +++ b/lib/config/customer_routes.dart @@ -1,5 +1,6 @@ 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/appointment_services_list_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/dashboard/dashboard_page.dart'; import 'package:flutter/cupertino.dart'; @@ -17,7 +18,8 @@ class CustomerAppRoutes { AppRoutes.appointmentDetailView: (context) => AppointmentDetailView(appointmentListModel: ModalRoute.of(context)!.settings.arguments as AppointmentListModel), AppRoutes.adsDetailView: (context) => AdsDetailView(adDetails: ModalRoute.of(context)!.settings.arguments as AdDetailsModel), AppRoutes.createAdView: (context) => CreateAdView(), - AppRoutes.bookAppointmenServicesView: (context) => BookAppointmentServicesView(), + AppRoutes.bookAppointmenServicesView: (context) => AppointmentServicesListView(), + AppRoutes.bookAppointmentsItemView: (context) => BookAppointmentsItemView(), AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(), }; } diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 2fb9aa2..94d836c 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.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_category_model.dart'; import 'package:mc_common_app/models/provider_service_model.dart'; @@ -29,9 +30,16 @@ class AppointmentsVM extends ChangeNotifier { } String pickedHomeLocation = ""; + String pickHomeLocationError = ""; void updatePickedHomeLocation(String value) { pickedHomeLocation = value; + pickHomeLocationError = ""; + notifyListeners(); + } + + void updatePickHomeLocationError(String value) { + pickHomeLocationError = value; notifyListeners(); } @@ -58,10 +66,25 @@ class AppointmentsVM extends ChangeNotifier { notifyListeners(); } + bool isServiceSelectionValidated() { + if (providerServiceId.selectedId == -1) { + return false; + } + + if (isHomeTapped) { + if (pickedHomeLocation == "") { + updatePickHomeLocationError(GlobalConsts.homeLocationEmptyError); + return false; + } + } + return true; + } + getProviderServices(int categoryId) async { providerServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); isHomeTapped = false; pickedHomeLocation = ""; + pickHomeLocationError = ""; if (categoryId != -1) { isFetchingServices = true; notifyListeners(); diff --git a/lib/views/appointments/appointment_services_list_view.dart b/lib/views/appointments/appointment_services_list_view.dart new file mode 100644 index 0000000..c2aae69 --- /dev/null +++ b/lib/views/appointments/appointment_services_list_view.dart @@ -0,0 +1,155 @@ +import 'package:car_customer_app/view_models/appointments_view_model.dart'; +import 'package:car_customer_app/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart'; +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/widgets_models.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/views/advertisement/custom_add_button.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/common_widgets/time_slots.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:provider/provider.dart'; + +class AppointmentServicesListView extends StatelessWidget { + AppointmentServicesListView({Key? key}) : super(key: key); + + void openTheAddServiceBottomSheet(BuildContext context, AppointmentsVM appointmentsVM) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return AppointmentServicePickBottomSheet(); + }, + ); + } + + final _dummySlots = [ + TimeSlotModel(isSelected: false, slotId: 1, slot: "11:00"), + TimeSlotModel(isSelected: false, slotId: 2, slot: "12:00"), + TimeSlotModel(isSelected: true, slotId: 3, slot: "13:00"), + TimeSlotModel(isSelected: false, slotId: 4, slot: "14:00"), + TimeSlotModel(isSelected: false, slotId: 5, slot: "15:00"), + TimeSlotModel(isSelected: false, slotId: 6, slot: "16:00"), + TimeSlotModel(isSelected: false, slotId: 7, slot: "17:00"), + TimeSlotModel(isSelected: false, slotId: 8, slot: "18:00"), + TimeSlotModel(isSelected: false, slotId: 9, slot: "19:00"), + TimeSlotModel(isSelected: false, slotId: 10, slot: "20:00"), + TimeSlotModel(isSelected: false, slotId: 11, slot: "21:00"), + TimeSlotModel(isSelected: false, slotId: 12, slot: "22:00"), + TimeSlotModel(isSelected: false, slotId: 13, slot: "23:00"), + TimeSlotModel(isSelected: false, slotId: 14, slot: "24:00"), + TimeSlotModel(isSelected: false, slotId: 15, slot: "25:00"), + TimeSlotModel(isSelected: false, slotId: 16, slot: "26:00"), + TimeSlotModel(isSelected: false, slotId: 17, slot: "27:00"), + TimeSlotModel(isSelected: false, slotId: 18, slot: "28:00"), + TimeSlotModel(isSelected: false, slotId: 19, slot: "29:00"), + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: "Book Appointment", + isRemoveBackButton: false, + isDrawerEnabled: false, + actions: [MyAssets.searchIcon.buildSvg().paddingOnly(right: 21)], + onBackButtonTapped: () => Navigator.pop(context), + ), + body: Consumer( + builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 21.height, + CustomAddButton( + onTap: () => openTheAddServiceBottomSheet(context, appointmentsVM), + text: "Add Services", + icon: Container( + height: 24, + width: 24, + decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor), + child: const Icon(Icons.add, color: MyColors.white), + ), + ).horPaddingMain(), + 10.height, + ListView.builder( + shrinkWrap: true, + itemCount: 20, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: "Mechanic".toText(fontSize: 16, isBold: true), + ), + Align( + alignment: Alignment.topRight, + child: MyAssets.icEdit.buildSvg(fit: BoxFit.fill, height: 19, width: 19), + ).onPress(() {}), + ], + ), + Builder(builder: (context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 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( + 5, + (index) => Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Gear Kit: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + ("200 SAR").toText(fontSize: 12, 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), + ], + ), + ], + if (true) ...[ + 8.height, + ("Available Slots").toText(fontSize: 14, isBold: true), + 5.height, + SizedBox( + width: double.infinity, + child: BuildTimeSlots( + timeSlots: _dummySlots, + onPressed: (index) => null, + ), + ), + ], + ], + ); + }), + ], + ).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 10)); + }, + ).expand() + ], + ); + }, + )); + } +} diff --git a/lib/views/appointments/book_appointment_services_view.dart b/lib/views/appointments/book_appointment_services_view.dart deleted file mode 100644 index 71f4bb3..0000000 --- a/lib/views/appointments/book_appointment_services_view.dart +++ /dev/null @@ -1,325 +0,0 @@ -import 'package:car_customer_app/view_models/appointments_view_model.dart'; -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/widgets_models.dart'; -import 'package:mc_common_app/theme/colors.dart'; -import 'package:mc_common_app/views/advertisement/custom_add_button.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/dropdown/dropdow_field.dart'; -import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; -import 'package:mc_common_app/widgets/txt_field.dart'; -import 'package:provider/provider.dart'; - -class BookAppointmentServicesView extends StatelessWidget { - const BookAppointmentServicesView({Key? key}) : super(key: key); - - void openTheAddServiceBottomSheet(BuildContext context) { - showModalBottomSheet( - context: context, - isScrollControlled: true, - enableDrag: true, - builder: (BuildContext context) { - AppointmentsVM appointmentsVM = context.watch(); - return SizedBox( - height: MediaQuery.of(context).size.height * 0.85, - child: Column( - children: [ - Container( - margin: const EdgeInsets.all(8), - height: 8, - width: 60, - decoration: const BoxDecoration(color: MyColors.lightTextColor, borderRadius: BorderRadius.all(Radius.circular(20))), - ), - 12.height, - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - "Select Category".toText(fontSize: 24, isBold: true), - ], - ), - 30.height, - Expanded( - child: ListView( - children: [ - Builder( - builder: (context) { - List serviceCategories = []; - for (var element in appointmentsVM.providerCategories) { - if (!element.isSelected!) { - serviceCategories.add(DropValue(element.id?.toInt() ?? 0, element.categoryName ?? "", "")); - } - } - return DropdownField( - (DropValue value) => appointmentsVM.updateProviderCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), - list: serviceCategories, - hint: "Select Category", - dropdownValue: appointmentsVM.providerCategoryId.selectedId != -1 - ? DropValue(appointmentsVM.providerCategoryId.selectedId, appointmentsVM.providerCategoryId.selectedOption, "") - : null, - ); - }, - ), - if (appointmentsVM.isFetchingServices) ...[ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [const CircularProgressIndicator().paddingAll(10)], - ), - ] else if (appointmentsVM.providerServices.isNotEmpty) ...[ - 8.height, - Builder( - builder: (context) { - List serviceCategories = []; - for (var element in appointmentsVM.providerServices) { - if (!element.isSelected!) { - serviceCategories.add(DropValue(element.id?.toInt() ?? 0, element.id.toString(), "")); - } - } - return DropdownField( - (DropValue value) => appointmentsVM.updateProviderServiceId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), - list: serviceCategories, - hint: "Select Services", - dropdownValue: appointmentsVM.providerServiceId.selectedId != -1 - ? DropValue(appointmentsVM.providerServiceId.selectedId, appointmentsVM.providerServiceId.selectedOption, "") - : null, - ); - }, - ), - ], - if (appointmentsVM.providerServiceId.selectedId != -1 && !appointmentsVM.isFetchingServices) ...[ - 16.height, - Row( - children: [ - "Select Service Location".toText( - fontSize: 16, - isBold: true, - color: MyColors.black, - ), - ], - ), - 8.height, - Row( - children: [ - Expanded( - child: ShowFillButton( - isFilled: appointmentsVM.isHomeTapped, - maxHeight: 48, - title: "Home", - txtColor: appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor, - onPressed: () => appointmentsVM.updateIsHomeTapped(true), - ), - ), - 12.width, - Expanded( - child: ShowFillButton( - isFilled: !appointmentsVM.isHomeTapped, - txtColor: !appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor, - maxHeight: 48, - title: "Workshop", - onPressed: () => appointmentsVM.updateIsHomeTapped(false), - ), - ), - ], - ), - if (appointmentsVM.isHomeTapped) ...[ - 8.height, - TxtField( - hint: 'Pick Home Location', - value: appointmentsVM.pickedHomeLocation, - isNeedClickAll: true, - postfixData: Icons.location_on, - postFixDataColor: MyColors.darkTextColor, - onTap: () { - //TODO: open the place picked to pick the location and save it in provider. - appointmentsVM.updatePickedHomeLocation("PM58+F97, Al Olaya, Riyadh 12333"); - }, - ), - 14.height, - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Icon( - Icons.warning, - color: MyColors.adPendingStatusColor, - size: 19, - ).paddingOnly(bottom: 2), - 3.width, - "Some services are not available on home location.".toText( - color: MyColors.adPendingStatusColor, - fontSize: 12, - isItalic: true, - ), - ], - ), - ] - ], - ], - ), - ), - SizedBox( - width: double.infinity, - child: Column( - children: [ - if (appointmentsVM.isHomeTapped && !appointmentsVM.isFetchingServices) ...[ - const Divider(thickness: 1, height: 1), - 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), - ], - ), - "These charges are additional to the actual service charges. For heavy items the charges may vary.".toText(fontSize: 12, color: MyColors.lightTextColor), - 22.height, - ], - SizedBox( - width: double.infinity, - child: ShowFillButton( - maxHeight: 55, - title: "Next", - onPressed: () {}, - ), - ), - ], - ), - ).paddingOnly(bottom: 20) - ], - )).horPaddingMain(); - }, - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: CustomAppBar( - title: "Book Appointment", - isRemoveBackButton: false, - isDrawerEnabled: false, - actions: [MyAssets.searchIcon.buildSvg().paddingOnly(right: 21)], - onBackButtonTapped: () => Navigator.pop(context), - ), - body: Consumer( - builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 21.height, - CustomAddButton( - onTap: () => {openTheAddServiceBottomSheet(context)}, - text: "Add Services", - icon: Container( - height: 24, - width: 24, - decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor), - child: const Icon(Icons.add, color: MyColors.white), - ), - ), - // 20.height, - // ListView.builder( - // physics: const NeverScrollableScrollPhysics(), - // shrinkWrap: true, - // itemCount: adVM.specialServiceCards.length, - // itemBuilder: (BuildContext context, int index) { - // SpecialServiceCard specialServicesCard = adVM.specialServiceCards[index]; - // return Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Column( - // children: [ - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Expanded( - // child: specialServicesCard.serviceSelectedId!.selectedOption.toText(fontSize: 16, isBold: true), - // ), - // Align( - // alignment: Alignment.topRight, - // child: MyAssets.closeWithOrangeBg.buildSvg( - // fit: BoxFit.fill, - // height: 30, - // width: 30, - // ), - // ).onPress(() => adVM.removeSpecialServiceCard(index)) - // ], - // ), - // Builder(builder: (context) { - // return Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // if (specialServicesCard.serviceSelectedId!.selectedId != 1 && specialServicesCard.serviceSelectedId!.selectedId != 3) ...[ - // // Row( - // // crossAxisAlignment: CrossAxisAlignment.start, - // // children: [ - // // "Duration: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - // // (specialServicesCard.duration ?? "").toText(fontSize: 12, isBold: true).expand(), - // // ], - // // ), - // 8.height, - // Row( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // "Description: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - // (specialServicesCard.description ?? "").toText(fontSize: 12, isBold: true).expand(), - // ], - // ), - // ], - // if (specialServicesCard.serviceSelectedId!.selectedId == 1 || specialServicesCard.serviceSelectedId!.selectedId == 3) ...[ - // Row( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // const Icon( - // weight: 2, - // Icons.location_on_outlined, - // color: MyColors.primaryColor, - // size: 17, - // ), - // "${specialServicesCard.duration} km".toText(fontSize: 10, color: MyColors.primaryColor), - // ], - // ), - // 8.height, - // Row( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // "Branch Address: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - // (specialServicesCard.address ?? "").toText(fontSize: 12, isBold: true).expand(), - // ], - // ), - // Row( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // "Appointment Time: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - // "${specialServicesCard.serviceDate} - ${specialServicesCard.serviceTime}".toText(fontSize: 12, isBold: true).expand(), - // ], - // ), - // ], - // 6.height, - // Row( - // crossAxisAlignment: CrossAxisAlignment.end, - // children: [ - // (specialServicesCard.serviceSelectedId!.itemPrice).toText(fontSize: 20, isBold: true), - // 2.width, - // "SAR".toText(color: MyColors.lightTextColor, fontSize: 14), - // ], - // ), - // ], - // ); - // }), - // 3.height, - // const Divider(thickness: 1.5) - // ], - // ), - // 10.height, - // ], - // ); - // }, - // ), - ], - ); - }, - ).horPaddingMain()); - } -} diff --git a/lib/views/appointments/book_appointments_item_view.dart b/lib/views/appointments/book_appointments_item_view.dart new file mode 100644 index 0000000..cacb5c7 --- /dev/null +++ b/lib/views/appointments/book_appointments_item_view.dart @@ -0,0 +1,113 @@ +import 'package:car_customer_app/view_models/appointments_view_model.dart'; +import 'package:car_customer_app/views/appointments/widgets/service_item_with_price_checkbox.dart'; +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/theme/colors.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 BookAppointmentsItemView extends StatelessWidget { + const BookAppointmentsItemView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: "Select Services", + isRemoveBackButton: false, + isDrawerEnabled: false, + actions: [MyAssets.searchIcon.buildSvg().paddingOnly(right: 21)], + onBackButtonTapped: () => Navigator.pop(context), + ), + body: Consumer( + builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 40, + width: double.infinity, + color: MyColors.darkTextColor, + alignment: Alignment.centerLeft, + child: "3 Items Selected".toText(fontSize: 16, color: MyColors.white).horPaddingMain(), + ), + 16.height, + Column( + children: [ + "Few services are not available on home location. Change the location to workshop to full access the services".toText(fontSize: 12, isItalic: true, color: MyColors.lightTextColor), + 8.height, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Change location or service: ".toText(fontSize: 14, isBold: true), + "Edit".toText(fontSize: 14, isBold: true, isUnderLine: true, color: MyColors.adPendingStatusColor), + 5.width, + MyAssets.icEdit.buildSvg(width: 17), + ], + ).onPress(() {}), + 16.height, + 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(), + Column( + children: [ + Divider( + height: 1, + thickness: 0.7, + ), + 16.height, + Row( + children: [ + Expanded( + child: ShowFillButton( + txtColor: MyColors.black, + maxHeight: 55, + title: "Cancel", + onPressed: () {}, + backgroundColor: MyColors.greyButtonColor, + ), + ), + 12.width, + Expanded( + child: ShowFillButton( + maxHeight: 55, + title: "Next", + onPressed: () {}, + backgroundColor: MyColors.darkPrimaryColor, + ), + ), + ], + ).horPaddingMain(), + 16.height, + ], + ), + ], + ); + }, + )); + } +} diff --git a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart new file mode 100644 index 0000000..7ab24fc --- /dev/null +++ b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart @@ -0,0 +1,194 @@ +import 'package:car_customer_app/view_models/appointments_view_model.dart'; +import 'package:flutter/material.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/widgets_models.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:mc_common_app/widgets/txt_field.dart'; +import 'package:provider/provider.dart'; + +class AppointmentServicePickBottomSheet extends StatelessWidget { + const AppointmentServicePickBottomSheet({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + AppointmentsVM appointmentsVM = context.watch(); + return SizedBox( + height: MediaQuery.of(context).size.height * 0.85, + child: Column( + children: [ + Container( + margin: const EdgeInsets.all(8), + height: 8, + width: 60, + decoration: const BoxDecoration(color: MyColors.lightTextColor, borderRadius: BorderRadius.all(Radius.circular(20))), + ), + 12.height, + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + "Select Category".toText(fontSize: 24, isBold: true), + ], + ), + 30.height, + Expanded( + child: ListView( + children: [ + Builder( + builder: (context) { + List serviceCategories = []; + for (var element in appointmentsVM.providerCategories) { + if (!element.isSelected!) { + serviceCategories.add(DropValue(element.id?.toInt() ?? 0, element.categoryName ?? "", "")); + } + } + return DropdownField( + (DropValue value) => appointmentsVM.updateProviderCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), + list: serviceCategories, + hint: "Select Category", + dropdownValue: appointmentsVM.providerCategoryId.selectedId != -1 + ? DropValue(appointmentsVM.providerCategoryId.selectedId, appointmentsVM.providerCategoryId.selectedOption, "") + : null, + ); + }, + ), + if (appointmentsVM.isFetchingServices) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [const CircularProgressIndicator().paddingAll(10)], + ), + ] else if (appointmentsVM.providerServices.isNotEmpty) ...[ + 8.height, + Builder( + builder: (context) { + List serviceCategories = []; + for (var element in appointmentsVM.providerServices) { + if (!element.isSelected!) { + serviceCategories.add(DropValue(element.id?.toInt() ?? 0, element.id.toString(), "")); + } + } + return DropdownField( + (DropValue value) => appointmentsVM.updateProviderServiceId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), + list: serviceCategories, + hint: "Select Services", + dropdownValue: appointmentsVM.providerServiceId.selectedId != -1 + ? DropValue(appointmentsVM.providerServiceId.selectedId, appointmentsVM.providerServiceId.selectedOption, "") + : null, + ); + }, + ), + ], + if (appointmentsVM.providerServiceId.selectedId != -1 && !appointmentsVM.isFetchingServices) ...[ + 16.height, + Row( + children: [ + "Select Service Location".toText( + fontSize: 16, + isBold: true, + color: MyColors.black, + ), + ], + ), + 8.height, + Row( + children: [ + Expanded( + child: ShowFillButton( + isFilled: appointmentsVM.isHomeTapped, + maxHeight: 48, + title: "Home", + txtColor: appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor, + onPressed: () => appointmentsVM.updateIsHomeTapped(true), + ), + ), + 12.width, + Expanded( + child: ShowFillButton( + isFilled: !appointmentsVM.isHomeTapped, + txtColor: !appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor, + maxHeight: 48, + title: "Workshop", + onPressed: () => appointmentsVM.updateIsHomeTapped(false), + ), + ), + ], + ), + if (appointmentsVM.isHomeTapped) ...[ + 8.height, + TxtField( + hint: 'Pick Home Location', + errorValue: appointmentsVM.pickHomeLocationError, + value: appointmentsVM.pickedHomeLocation, + isNeedClickAll: true, + postfixData: Icons.location_on, + postFixDataColor: MyColors.darkTextColor, + onTap: () { + //TODO: open the place picked to pick the location and save it in provider. + appointmentsVM.updatePickedHomeLocation("PM58+F97, Al Olaya, Riyadh 12333"); + }, + ), + 14.height, + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Icon( + Icons.warning, + color: MyColors.adPendingStatusColor, + size: 19, + ).paddingOnly(bottom: 2), + 3.width, + "Some services are not available on home location.".toText( + color: MyColors.adPendingStatusColor, + fontSize: 12, + isItalic: true, + ), + ], + ), + ] + ], + ], + ), + ), + SizedBox( + width: double.infinity, + child: Column( + children: [ + if (appointmentsVM.isHomeTapped && !appointmentsVM.isFetchingServices) ...[ + const Divider(thickness: 1, height: 1), + 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), + ], + ), + "These charges are additional to the actual service charges. For heavy items the charges may vary.".toText(fontSize: 12, color: MyColors.lightTextColor), + 22.height, + ], + SizedBox( + width: double.infinity, + child: ShowFillButton( + maxHeight: 55, + backgroundColor: !appointmentsVM.isServiceSelectionValidated() ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.primaryColor, + title: "Next", + onPressed: () { + bool isValidated = appointmentsVM.isServiceSelectionValidated(); + if (isValidated) { + navigateWithName(context, AppRoutes.bookAppointmentsItemView); + } + }, + ), + ), + ], + ), + ).paddingOnly(bottom: 20) + ], + )).horPaddingMain(); + } +} diff --git a/lib/views/appointments/widgets/service_item_with_price_checkbox.dart b/lib/views/appointments/widgets/service_item_with_price_checkbox.dart new file mode 100644 index 0000000..f8269c6 --- /dev/null +++ b/lib/views/appointments/widgets/service_item_with_price_checkbox.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; + +class ServiceItemWithPriceCheckBox extends StatelessWidget { + final bool isSelected; + final String title, description; + final Widget priceWidget; + + final Function(bool) onSelection; + + const ServiceItemWithPriceCheckBox({ + required this.isSelected, + required this.title, + required this.description, + this.priceWidget = const SizedBox(), + required this.onSelection, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: double.infinity, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Checkbox( + value: isSelected, + onChanged: (bool? v) { + onSelection(v ?? false); + }, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + title.toText(fontSize: 16, isBold: true).paddingOnly(top: 10), + description.toText(fontSize: 12, color: MyColors.lightTextColor), + priceWidget, + ], + ), + ), + ], + ), + ); + } +} From 3390112aa3b3d0184e9f594155a3158e42be08db Mon Sep 17 00:00:00 2001 From: FaizHashmiCS22 Date: Wed, 14 Jun 2023 15:20:49 +0300 Subject: [PATCH 02/12] test commit to GitTea --- lib/main.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/main.dart b/lib/main.dart index f518977..3bef3f4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -26,6 +26,8 @@ import 'package:provider/provider.dart'; import 'package:provider/single_child_widget.dart'; import 'package:sizer/sizer.dart'; + +//test commit Future main() async { WidgetsFlutterBinding.ensureInitialized(); CustomerDependencies.addDependencies(); From 3f0178e1cd04c4509c319d9df182a3d7e6a81c33 Mon Sep 17 00:00:00 2001 From: FaizHashmiCS22 Date: Thu, 15 Jun 2023 17:23:29 +0300 Subject: [PATCH 03/12] Completed the new design for the Appointment Flow --- lib/config/customer_routes.dart | 2 + .../appointment_services_list_view.dart | 103 ++++++- .../appointments/review_appointment_view.dart | 265 ++++++++++++++++++ .../dashboard/fragments/ads_fragment.dart | 4 +- 4 files changed, 371 insertions(+), 3 deletions(-) create mode 100644 lib/views/appointments/review_appointment_view.dart diff --git a/lib/config/customer_routes.dart b/lib/config/customer_routes.dart index bcf7484..bcbb4e0 100644 --- a/lib/config/customer_routes.dart +++ b/lib/config/customer_routes.dart @@ -2,6 +2,7 @@ import 'package:car_customer_app/views/appointments/appointment_detail_view.dart import 'package:car_customer_app/views/appointments/appointment_services_list_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:flutter/cupertino.dart'; import 'package:mc_common_app/config/routes.dart'; @@ -20,6 +21,7 @@ class CustomerAppRoutes { AppRoutes.createAdView: (context) => CreateAdView(), AppRoutes.bookAppointmenServicesView: (context) => AppointmentServicesListView(), AppRoutes.bookAppointmentsItemView: (context) => BookAppointmentsItemView(), + AppRoutes.reviewAppointmentView: (context) => ReviewAppointment(), AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(), }; } diff --git a/lib/views/appointments/appointment_services_list_view.dart b/lib/views/appointments/appointment_services_list_view.dart index c2aae69..3d6355c 100644 --- a/lib/views/appointments/appointment_services_list_view.dart +++ b/lib/views/appointments/appointment_services_list_view.dart @@ -2,12 +2,16 @@ import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:car_customer_app/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart'; 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/widgets_models.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/views/advertisement/custom_add_button.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/common_widgets/info_bottom_sheet.dart'; import 'package:mc_common_app/widgets/common_widgets/time_slots.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; @@ -48,6 +52,73 @@ class AppointmentServicesListView extends StatelessWidget { TimeSlotModel(isSelected: false, slotId: 19, slot: "29:00"), ]; + void priceBreakDownClicked(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return InfoBottomSheet( + title: "Charges Breakdown", + description: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Services".toText(fontSize: 16, isBold: true), + Column( + children: List.generate( + 5, + (index) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Gear Kit".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + "200 SAR".toText(fontSize: 12, isBold: true), + ], + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + "1149 SAR".toText(fontSize: 16, isBold: true), + ], + ), + 20.height, + "Home Location".toText(fontSize: 16, isBold: true), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "10km ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + "5 x 10".toText(fontSize: 12, isBold: true), + ], + ), + 8.height, + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + "50 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: [ + "120".toText(fontSize: 29, isBold: true), + 2.width, + "SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), + ], + ) + ], + ), + 30.height, + ], + )); + }); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -125,8 +196,12 @@ class AppointmentServicesListView extends StatelessWidget { "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)), ], if (true) ...[ 8.height, @@ -146,7 +221,31 @@ class AppointmentServicesListView extends StatelessWidget { ], ).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 10)); }, - ).expand() + ).expand(), + Row( + children: [ + Expanded( + child: ShowFillButton( + txtColor: MyColors.black, + maxHeight: 55, + title: "Cancel", + onPressed: () {}, + backgroundColor: MyColors.greyButtonColor, + ), + ), + 12.width, + Expanded( + child: ShowFillButton( + maxHeight: 55, + title: "Review", + onPressed: () { + navigateWithName(context, AppRoutes.reviewAppointmentView); + }, + backgroundColor: MyColors.darkPrimaryColor, + ), + ) + ], + ).paddingAll(21) ], ); }, diff --git a/lib/views/appointments/review_appointment_view.dart b/lib/views/appointments/review_appointment_view.dart new file mode 100644 index 0000000..96c8a88 --- /dev/null +++ b/lib/views/appointments/review_appointment_view.dart @@ -0,0 +1,265 @@ +import 'package:easy_localization/easy_localization.dart'; +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/generated/locale_keys.g.dart'; +import 'package:mc_common_app/theme/colors.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'; + +class ReviewAppointment extends StatelessWidget { + const ReviewAppointment({Key? key}) : super(key: key); + + Widget buildBranchInfoCard({ + required String branchImageUrl, + required String branchName, + required String branchRatings, + required String branchLocation, + }) { + return Padding( + padding: const EdgeInsets.only( + bottom: 10, + left: 21, + right: 21, + ), + child: Row( + children: [ + Image.asset( + branchImageUrl, + width: 80, + height: 60, + fit: BoxFit.cover, + ), + 12.width, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + branchName.toText(fontSize: 16, isBold: true), + Row( + children: [ + LocaleKeys.location.tr().toText(color: MyColors.lightTextColor, fontSize: 12), + 2.width, + ":$branchLocation".toText(fontSize: 12), + ], + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + branchRatings.toText( + isUnderLine: true, + isBold: true, + fontSize: 12, + ), + 2.width, + MyAssets.starIcon.buildSvg(width: 12), + ], + ), + ], + ), + ], + ), + ), + ], + ).onPress(() {}).toWhiteContainer(width: double.infinity, allPading: 12)); + } + + Widget buildServicesSubSection({required String title, required List itemList}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title.toText(fontSize: 16, isBold: true), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: List.generate( + itemList.length, + (index) => itemList[index].toText( + fontSize: 12, + color: MyColors.lightTextColor, + isBold: true, + )), + ), + ], + ).paddingOnly(bottom: 14); + } + + Widget buildServicesInfoCard() { + return SizedBox( + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: List.generate( + 2, + (index) => buildServicesSubSection( + title: "Mechanic", + itemList: [ + "Engine Check : 450 SAR", + "Gear Check : 700 SAR", + "Gear Check : 700 SAR", + "Gear Check : 700 SAR", + ], + ), + ), + ).toWhiteContainer(width: double.infinity, allPading: 12, margin: EdgeInsets.symmetric(horizontal: 21)), + ); + } + + Widget buildTimeAndLocationInfoCard() { + return SizedBox( + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Time & Location".toText(fontSize: 16, isBold: true), + 3.height, + Row(children: [ + "Date & Time: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + "2nd Feb, 2023 at 09:30 AM".toText(fontSize: 12, isBold: true), + ]), + Row(children: [ + "Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + "PM58+F97, Al Olaya, Riyadh 12333".toText(fontSize: 12, isBold: true), + ]), + ], + ).toWhiteContainer(width: double.infinity, allPading: 12, margin: EdgeInsets.only(left: 21, right: 21, top: 10)), + ); + } + + Widget buildChargesBreakDown() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Services".toText(fontSize: 16, isBold: true), + Column( + children: List.generate( + 5, + (index) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Gear Kit".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + "200 SAR".toText(fontSize: 12, isBold: true), + ], + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + "1149 SAR".toText(fontSize: 16, isBold: true), + ], + ), + 10.height, + Divider( + thickness: 0.7, + ), + "Home Location".toText(fontSize: 16, isBold: true), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "10km ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + "5 x 10".toText(fontSize: 12, isBold: true), + ], + ), + 8.height, + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + "50 SAR".toText(fontSize: 16, isBold: true), + ], + ), + 10.height, + Divider( + thickness: 0.7, + ), + 10.height, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Total Amount ".toText(fontSize: 16, isBold: true), + 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), + ], + ) + ], + ), + 10.height, + ], + ).toWhiteContainer(width: double.infinity, allPading: 12, margin: EdgeInsets.only(left: 21, right: 21, top: 10, bottom: 21)); + } + + Widget buildNextButtonFooter() { + return Container( + height: 90, + color: MyColors.white, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Divider(thickness: 0.7, height: 3), + 10.height, + SizedBox( + width: double.infinity, + child: ShowFillButton( + maxHeight: 55, + backgroundColor: MyColors.primaryColor, + title: "Book Appointment", + onPressed: () {}, + ).paddingOnly(bottom: 12, left: 21, right: 21), + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: "Review Appointment", + isRemoveBackButton: false, + isDrawerEnabled: false, + actions: [MyAssets.searchIcon.buildSvg().paddingOnly(right: 21)], + onBackButtonTapped: () => Navigator.pop(context), + ), + body: Stack( + alignment: Alignment.bottomCenter, + children: [ + SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildBranchInfoCard( + branchImageUrl: MyAssets.bnCar, + branchLocation: " 3km", + branchName: "Al Ahmed Maintenance", + branchRatings: "4.9", + ), + buildServicesInfoCard(), + buildTimeAndLocationInfoCard(), + buildChargesBreakDown(), + ], + ), + ), + buildNextButtonFooter(), + ], + ), + ); + } +} diff --git a/lib/views/dashboard/fragments/ads_fragment.dart b/lib/views/dashboard/fragments/ads_fragment.dart index d13952c..3201ffa 100644 --- a/lib/views/dashboard/fragments/ads_fragment.dart +++ b/lib/views/dashboard/fragments/ads_fragment.dart @@ -103,7 +103,9 @@ class AdsFragment extends StatelessWidget { ), floatingActionButton: FloatingActionButton( onPressed: () { - context.read().getVehicleTypes(); + if (context.read().vehicleTypes.isEmpty) { + context.read().getVehicleTypes(); + } navigateWithName(context, AppRoutes.createAdView); }, From 8bc13dff3427dd9689984d888ea1ba673f421425 Mon Sep 17 00:00:00 2001 From: FaizHashmiCS22 Date: Tue, 20 Jun 2023 15:55:55 +0300 Subject: [PATCH 04/12] Updated the New Models --- lib/config/customer_dependencies.dart | 2 +- lib/config/customer_routes.dart | 4 +- lib/main.dart | 8 +- lib/repositories/provider_repo.dart | 37 +++--- lib/view_models/appointments_view_model.dart | 114 +++++++++++++----- lib/view_models/providers_view_model.dart | 85 +++---------- ...appointment_service_pick_bottom_sheet.dart | 14 +-- lib/views/dashboard/dashboard_page.dart | 8 +- .../fragments/appointments_fragment.dart | 6 +- .../fragments/branches_fragment.dart | 60 +++++---- .../dashboard/fragments/home_fragment.dart | 2 +- .../fragments/providers_fragment.dart | 7 +- lib/views/provider/branch_detail_page.dart | 49 ++++---- lib/views/provider/provider_profile_page.dart | 12 +- .../provider/sheet/items_list_sheet.dart | 13 +- pubspec.yaml | 2 +- 16 files changed, 212 insertions(+), 211 deletions(-) diff --git a/lib/config/customer_dependencies.dart b/lib/config/customer_dependencies.dart index 3debdff..b41d76a 100644 --- a/lib/config/customer_dependencies.dart +++ b/lib/config/customer_dependencies.dart @@ -4,7 +4,7 @@ import 'package:mc_common_app/config/dependencies.dart'; class CustomerDependencies { static void addDependencies() { AppDependencies.addDependencies(); - injector.registerSingleton(() => ProviderRepoImp()); + injector.registerSingleton(() => BranchRepoImp()); } } diff --git a/lib/config/customer_routes.dart b/lib/config/customer_routes.dart index b524a29..b90265b 100644 --- a/lib/config/customer_routes.dart +++ b/lib/config/customer_routes.dart @@ -10,7 +10,7 @@ import 'package:flutter/cupertino.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; -import 'package:mc_common_app/models/services/branch_model.dart'; +import 'package:mc_common_app/models/branch_models/branch_detail_model.dart'; import 'package:mc_common_app/views/advertisement/ads_detail_view.dart'; import 'package:mc_common_app/views/advertisement/create_ad_view.dart'; import 'package:mc_common_app/views/payments/payment_methods_view.dart'; @@ -26,7 +26,7 @@ class CustomerAppRoutes { AppRoutes.bookAppointmentsItemView: (context) => BookAppointmentsItemView(), AppRoutes.reviewAppointmentView: (context) => ReviewAppointment(), AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(), - AppRoutes.branchDetailPage: (context) => BranchDetailPage(branchModel: ModalRoute.of(context)!.settings.arguments as BranchModel), + 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 ae0ad10..d258ca2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -55,16 +55,10 @@ Future main() async { adsRepo: injector.get(), ), ), - ChangeNotifierProvider( - create: (_) => ProvidersVM( - commonServices: injector.get(), - commonRepo: injector.get(), - providerRepo: injector.get(), - ), - ), ChangeNotifierProvider( create: (_) => AppointmentsVM( commonServices: injector.get(), + branchRepo: injector.get(), commonRepo: injector.get(), ), ), diff --git a/lib/repositories/provider_repo.dart b/lib/repositories/provider_repo.dart index d92a66d..dc057f3 100644 --- a/lib/repositories/provider_repo.dart +++ b/lib/repositories/provider_repo.dart @@ -1,33 +1,38 @@ import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'package:mc_common_app/models/model/provider_model.dart'; -import 'package:mc_common_app/models/services/item_model.dart'; -import 'package:mc_common_app/models/services/near_branch_model.dart'; -import 'package:http/http.dart' as http; +import 'package:flutter/cupertino.dart'; 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/branch_models/branch_detail_model.dart'; +import 'package:mc_common_app/models/generic_resp_model.dart'; +import 'package:mc_common_app/models/model/provider_model.dart'; +import 'package:mc_common_app/models/services/item_model.dart'; -import 'package:flutter/cupertino.dart'; - -abstract class ProviderRepo { - Future getAllNearBranchAndServices(); +abstract class BranchRepo { + Future> getAllNearBranchAndServices(); Future getServiceItems(int serviceId); Future getBranchAndServices(int providerId); } -class ProviderRepoImp implements ProviderRepo { +class BranchRepoImp implements BranchRepo { + ApiClient apiClient = injector.get(); + AppState appState = injector.get(); + @override - Future getAllNearBranchAndServices() async { - // var postParams = {"serviceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? ""}; - String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().getJsonForObject((json) => NearBrancheModel.fromJson(json), ApiConsts.GetAllNearBranches, token: t); + Future> getAllNearBranchAndServices() async { + GenericRespModel adsGenericModel = await apiClient.getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.GetAllNearBranches, + token: appState.getUser.data!.accessToken, + ); + + List branches = List.generate(adsGenericModel.data.length, (index) => BranchDetailModel.fromJson(adsGenericModel.data[index])); + + return branches; } @override diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 94d836c..90afeb1 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -1,17 +1,71 @@ -import 'package:flutter/cupertino.dart'; +import 'package:car_customer_app/repositories/provider_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/branch_models/branch_detail_model.dart'; import 'package:mc_common_app/models/provider_category_model.dart'; -import 'package:mc_common_app/models/provider_service_model.dart'; import 'package:mc_common_app/models/widgets_models.dart'; import 'package:mc_common_app/repositories/common_repo.dart'; import 'package:mc_common_app/services/common_services.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/view_models/base_view_model.dart'; +import 'package:mc_common_app/models/services/item_model.dart'; -class AppointmentsVM extends ChangeNotifier { + +class AppointmentsVM extends BaseVM { final CommonRepo commonRepo; + final BranchRepo branchRepo; final CommonAppServices commonServices; - AppointmentsVM({required this.commonServices, required this.commonRepo}); + AppointmentsVM({required this.commonServices, required this.branchRepo, required this.commonRepo}); + + List providersFilterOptions = []; + List nearbyBranchesList = []; + ItemModel? serviceItems; + ProviderModel? providerModel; + + populateProvidersFilterList() { + providersFilterOptions.clear(); + providersFilterOptions = [ + FilterListModel(title: "All Branches", isSelected: true, id: -1), + FilterListModel(title: "Maintenance", isSelected: false, id: 0), + FilterListModel(title: "Oil Service", isSelected: false, id: 1), + FilterListModel(title: "Accessories", isSelected: false, id: 2), + FilterListModel(title: "Tire Service", isSelected: false, id: 3), + FilterListModel(title: "Dent and Paint", isSelected: false, id: 4), + ]; + notifyListeners(); + } + + applyFilterOnProviders({required int index}) { + if (providersFilterOptions.isEmpty) return; + for (var value in providersFilterOptions) { + value.isSelected = false; + } + providersFilterOptions[index].isSelected = true; + notifyListeners(); + } + + //Create new branch + getAllNearBranches({bool isNeedToRebuild = false}) async { + //TODO: needs to lat,long into API + nearbyBranchesList.clear(); + if (isNeedToRebuild) setState(ViewState.busy); + nearbyBranchesList = await branchRepo.getAllNearBranchAndServices(); + setState(ViewState.idle); + } + + Future getServiceItems(int serviceId) async { + serviceItems = null; + serviceItems = await branchRepo.getServiceItems(serviceId); + setState(ViewState.idle); + return serviceItems; + } + + getBranchAndServices(int providerId) async { + providerModel = null; + providerModel = await branchRepo.getBranchAndServices(providerId); + setState(ViewState.idle); + } bool isFetchingLists = false; @@ -20,7 +74,7 @@ class AppointmentsVM extends ChangeNotifier { bool isFetchingServices = false; - List providerCategories = []; + List branchCategories = []; bool isHomeTapped = false; @@ -47,27 +101,48 @@ class AppointmentsVM extends ChangeNotifier { void updateProviderCategoryId(SelectionModel id) async { providerCategoryId = id; - await getProviderServices(id.selectedId); + await getBranchServices(id.selectedId); notifyListeners(); } - List providerServices = []; + List branchServices = []; - SelectionModel providerServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel branchServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateProviderServiceId(SelectionModel id) async { - providerServiceId = id; + branchServiceId = id; notifyListeners(); } - void getProviderCategories() async { + void getBranchCategories() async { notifyListeners(); - providerCategories = await commonRepo.getProviderServiceCategories(); + branchCategories = await commonRepo.getProviderServiceCategories(); notifyListeners(); } + getBranchServices(int categoryId) async { + branchServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + isHomeTapped = false; + pickedHomeLocation = ""; + pickHomeLocationError = ""; + if (categoryId != -1) { + isFetchingServices = true; + notifyListeners(); + branchServices = getFilteredBranchServices(categoryId: categoryId, branchId: 6); + isFetchingServices = false; + + notifyListeners(); + } + } + + List getFilteredBranchServices({required int branchId, required int categoryId}) { + + // List filteredServices = nearbyBranchesList.where((element) => element) + return []; + } + bool isServiceSelectionValidated() { - if (providerServiceId.selectedId == -1) { + if (branchServiceId.selectedId == -1) { return false; } @@ -80,21 +155,6 @@ class AppointmentsVM extends ChangeNotifier { return true; } - getProviderServices(int categoryId) async { - providerServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - isHomeTapped = false; - pickedHomeLocation = ""; - pickHomeLocationError = ""; - if (categoryId != -1) { - isFetchingServices = true; - notifyListeners(); - providerServices = await commonRepo.getProviderServices(categoryId: categoryId); - isFetchingServices = false; - - notifyListeners(); - } - } - populateAppointmentsFilterList() { appointmentsFilterOptions.clear(); appointmentsFilterOptions = [ diff --git a/lib/view_models/providers_view_model.dart b/lib/view_models/providers_view_model.dart index 568f713..6083eef 100644 --- a/lib/view_models/providers_view_model.dart +++ b/lib/view_models/providers_view_model.dart @@ -1,67 +1,18 @@ -import 'package:mc_common_app/models/model/provider_model.dart'; -import 'package:mc_common_app/models/services/item_model.dart'; -import 'package:mc_common_app/models/services/near_branch_model.dart'; -import 'package:car_customer_app/repositories/provider_repo.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:mc_common_app/models/widgets_models.dart'; -import 'package:mc_common_app/repositories/common_repo.dart'; -import 'package:mc_common_app/services/common_services.dart'; -import 'package:mc_common_app/utils/enums.dart'; -import 'package:mc_common_app/view_models/base_view_model.dart'; - -class ProvidersVM extends BaseVM { - final CommonRepo commonRepo; - final CommonAppServices commonServices; - final ProviderRepo providerRepo; - - ProvidersVM({required this.commonServices, required this.commonRepo, required this.providerRepo}); - - List providersFilterOptions = []; - NearBrancheModel? branches; - ItemModel? serviceItems; - ProviderModel? providerModel; - - populateProvidersFilterList() { - providersFilterOptions.clear(); - providersFilterOptions = [ - FilterListModel(title: "All Providers", isSelected: true, id: -1), - FilterListModel(title: "Maintenance", isSelected: false, id: 0), - FilterListModel(title: "Oil Service", isSelected: false, id: 1), - FilterListModel(title: "Accessories", isSelected: false, id: 2), - FilterListModel(title: "Tire Service", isSelected: false, id: 3), - FilterListModel(title: "Dent and Paint", isSelected: false, id: 4), - ]; - notifyListeners(); - } - - applyFilterOnProviders({required int index}) { - if (providersFilterOptions.isEmpty) return; - for (var value in providersFilterOptions) { - value.isSelected = false; - } - providersFilterOptions[index].isSelected = true; - notifyListeners(); - } - - //Create new branch - getAllNearBranches({bool isNeedToRebuild = false}) async { - //TODO: needs to lat,long into API - branches = null; - if (isNeedToRebuild) setState(ViewState.busy); - branches = await providerRepo.getAllNearBranchAndServices(); - setState(ViewState.idle); - } - - Future getServiceItems(int serviceId) async { - serviceItems = null; - serviceItems = await providerRepo.getServiceItems(serviceId); - setState(ViewState.idle); - return serviceItems; - } - - getBranchAndServices(int providerId) async { - providerModel = null; - providerModel = await providerRepo.getBranchAndServices(providerId); - setState(ViewState.idle); - } -} +// import 'package:car_customer_app/repositories/provider_repo.dart'; +// import 'package:mc_common_app/models/branch_models/branch_detail_model.dart'; +// import 'package:mc_common_app/models/model/provider_model.dart'; +// import 'package:mc_common_app/models/services/item_model.dart'; +// import 'package:mc_common_app/models/widgets_models.dart'; +// import 'package:mc_common_app/repositories/common_repo.dart'; +// import 'package:mc_common_app/services/common_services.dart'; +// import 'package:mc_common_app/utils/enums.dart'; +// import 'package:mc_common_app/view_models/base_view_model.dart'; + +// class ProvidersVM extends BaseVM { +// final CommonRepo commonRepo; +// final CommonAppServices commonServices; +// final ProviderRepo providerRepo; +// +// ProvidersVM({required this.commonServices, required this.commonRepo, required this.providerRepo}); +// +// } 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 7ab24fc..6ea7081 100644 --- a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart +++ b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart @@ -42,7 +42,7 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { Builder( builder: (context) { List serviceCategories = []; - for (var element in appointmentsVM.providerCategories) { + for (var element in appointmentsVM.branchCategories) { if (!element.isSelected!) { serviceCategories.add(DropValue(element.id?.toInt() ?? 0, element.categoryName ?? "", "")); } @@ -62,28 +62,28 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [const CircularProgressIndicator().paddingAll(10)], ), - ] else if (appointmentsVM.providerServices.isNotEmpty) ...[ + ] else if (appointmentsVM.branchServices.isNotEmpty) ...[ 8.height, Builder( builder: (context) { List serviceCategories = []; - for (var element in appointmentsVM.providerServices) { + for (var element in appointmentsVM.branchServices) { if (!element.isSelected!) { - serviceCategories.add(DropValue(element.id?.toInt() ?? 0, element.id.toString(), "")); + serviceCategories.add(DropValue(element.providerBranchServiceID ?? 0, element.providerBranchServiceID.toString(), "")); } } return DropdownField( (DropValue value) => appointmentsVM.updateProviderServiceId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), list: serviceCategories, hint: "Select Services", - dropdownValue: appointmentsVM.providerServiceId.selectedId != -1 - ? DropValue(appointmentsVM.providerServiceId.selectedId, appointmentsVM.providerServiceId.selectedOption, "") + dropdownValue: appointmentsVM.branchServiceId.selectedId != -1 + ? DropValue(appointmentsVM.branchServiceId.selectedId, appointmentsVM.branchServiceId.selectedOption, "") : null, ); }, ), ], - if (appointmentsVM.providerServiceId.selectedId != -1 && !appointmentsVM.isFetchingServices) ...[ + if (appointmentsVM.branchServiceId.selectedId != -1 && !appointmentsVM.isFetchingServices) ...[ 16.height, Row( children: [ diff --git a/lib/views/dashboard/dashboard_page.dart b/lib/views/dashboard/dashboard_page.dart index 1c9fa86..8b55571 100644 --- a/lib/views/dashboard/dashboard_page.dart +++ b/lib/views/dashboard/dashboard_page.dart @@ -2,11 +2,9 @@ import 'dart:async'; import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:car_customer_app/view_models/dashboard_view_model.dart'; -import 'package:car_customer_app/view_models/providers_view_model.dart'; import 'package:car_customer_app/views/dashboard/fragments/ads_fragment.dart'; import 'package:car_customer_app/views/dashboard/fragments/appointments_fragment.dart'; import 'package:car_customer_app/views/dashboard/fragments/home_fragment.dart'; -import 'package:car_customer_app/views/dashboard/fragments/providers_fragment.dart'; import 'package:car_customer_app/views/dashboard/fragments/settings_fragment.dart'; import 'package:car_customer_app/views/dashboard/widgets/bottom_nav_bar.dart'; import 'package:car_customer_app/views/dashboard/widgets/drawer_widget.dart'; @@ -36,9 +34,9 @@ class _DashboardPageState extends State { fetchUsername(); scheduleMicrotask(() { context.read().populateAppointmentsFilterList(); - context.read().populateProvidersFilterList(); + context.read().populateProvidersFilterList(); context.read().populateAdsFilterList(); - context.read().getAllNearBranches(); + context.read().getAllNearBranches(); _onRefresh(); }); } @@ -69,7 +67,7 @@ class _DashboardPageState extends State { String getPageTitle(int index) { if (index == 0) { - return "Branchs"; + return "Branches"; } if (index == 1) { return "Appointments"; diff --git a/lib/views/dashboard/fragments/appointments_fragment.dart b/lib/views/dashboard/fragments/appointments_fragment.dart index 137ae1e..aff1704 100644 --- a/lib/views/dashboard/fragments/appointments_fragment.dart +++ b/lib/views/dashboard/fragments/appointments_fragment.dart @@ -41,7 +41,11 @@ class AppointmentsFragment extends StatelessWidget { itemCount: appointmentsVM.myAppointments.length, itemBuilder: (BuildContext context, int index) { return BuildAppointmentContainerForCustomer( - onTapped: () => navigateWithName(context, AppRoutes.appointmentDetailView, arguments: appointmentsVM.myAppointments[index]), + onTapped: () => navigateWithName( + context, + AppRoutes.appointmentDetailView, + arguments: appointmentsVM.myAppointments[index], + ), appointmentListModel: appointmentsVM.myAppointments[index], ); }, diff --git a/lib/views/dashboard/fragments/branches_fragment.dart b/lib/views/dashboard/fragments/branches_fragment.dart index d0ab34c..f94231e 100644 --- a/lib/views/dashboard/fragments/branches_fragment.dart +++ b/lib/views/dashboard/fragments/branches_fragment.dart @@ -1,17 +1,16 @@ -import 'package:car_customer_app/view_models/providers_view_model.dart'; +import 'package:car_customer_app/view_models/appointments_view_model.dart'; 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/generated/locale_keys.g.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/models/branch_models/branch_detail_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/widgets/common_widgets/categories_list.dart'; import 'package:mc_common_app/widgets/common_widgets/provider_details_card.dart'; import 'package:provider/provider.dart'; -import 'package:easy_localization/easy_localization.dart'; class BranchesFragment extends StatelessWidget { const BranchesFragment({Key? key}) : super(key: key); @@ -23,7 +22,7 @@ class BranchesFragment extends StatelessWidget { width: double.infinity, height: double.infinity, child: Consumer( - builder: (BuildContext context, ProvidersVM providersVM, Widget? child) { + builder: (BuildContext context, AppointmentsVM providersVM, Widget? child) { return Column( children: [ 16.height, @@ -35,41 +34,40 @@ class BranchesFragment extends StatelessWidget { Expanded( child: RefreshIndicator( onRefresh: () async { - context.read().getAllNearBranches(isNeedToRebuild: true); + context.read().getAllNearBranches(isNeedToRebuild: true); }, child: SingleChildScrollView( child: Container( width: double.infinity, height: MediaQuery.of(context).size.height / 1.37, - child: Consumer( - builder: (context, model, _) { - if (model.state == ViewState.busy) { + child: Consumer( + builder: (context, viewModel, _) { + if (viewModel.state == ViewState.busy) { return const Center(child: CircularProgressIndicator()); } else { - return model.branches?.data == null - ? const Center(child: Text("No Branch Found")) - : model.branches!.data!.isEmpty - ? Center(child: Text(LocaleKeys.no_branch.tr())) - : ListView.separated( - itemBuilder: (context, index) { - return ProviderDetailsCard( - onCardTapped: () { - navigateWithName(context, AppRoutes.branchDetailPage, arguments: model.branches!.data![index]); - }, - providerImageUrl: MyAssets.bnCar, - title: model.branches!.data![index].branchName ?? "", - providerLocation: model.branches!.data![index].distanceKm.toString() + " KM", - providerName: model.branches!.data![index].serviceProviderName ?? "", - providerRatings: "4.9", - items: model.branches!.data![index].branchServices, - ); - }, - separatorBuilder: (context, index) { - return 12.height; + return viewModel.nearbyBranchesList.isEmpty + ? Center(child: "No Branches to show.".toText(fontSize: 16, color: MyColors.lightTextColor)) + : ListView.separated( + itemCount: viewModel.nearbyBranchesList.length, + itemBuilder: (context, index) { + BranchDetailModel branchDetailModel = viewModel.nearbyBranchesList[index]; + return ProviderDetailsCard( + onCardTapped: () { + navigateWithName(context, AppRoutes.branchDetailPage, arguments: branchDetailModel); }, - itemCount: model.branches!.data!.length, - padding: const EdgeInsets.all(12), + providerImageUrl: MyAssets.bnCar, + title: branchDetailModel.branchName ?? "", + providerLocation: branchDetailModel.distanceKM.toString() + " KM", + providerName: branchDetailModel.serviceProviderName ?? "", + providerRatings: "4.9", + items: branchDetailModel.branchServices, ); + }, + separatorBuilder: (context, index) { + return 12.height; + }, + padding: const EdgeInsets.all(12), + ); } }, ), diff --git a/lib/views/dashboard/fragments/home_fragment.dart b/lib/views/dashboard/fragments/home_fragment.dart index c7c4d6b..158416e 100644 --- a/lib/views/dashboard/fragments/home_fragment.dart +++ b/lib/views/dashboard/fragments/home_fragment.dart @@ -43,7 +43,7 @@ class HomeFragment extends StatelessWidget { onSubtitleTapped: () { print("value: ${AppState().getUser.data!.accessToken}"); context.read().onNavbarTapped(0); - context.read().applyFilterOnProviders(index: 0); + context.read().applyFilterOnProviders(index: 0); }).horPaddingMain(), const ServiceProviderWidget().horPaddingMain(), Consumer( diff --git a/lib/views/dashboard/fragments/providers_fragment.dart b/lib/views/dashboard/fragments/providers_fragment.dart index 09e6dab..b749bd0 100644 --- a/lib/views/dashboard/fragments/providers_fragment.dart +++ b/lib/views/dashboard/fragments/providers_fragment.dart @@ -1,5 +1,4 @@ import 'package:car_customer_app/view_models/appointments_view_model.dart'; -import 'package:car_customer_app/view_models/providers_view_model.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; @@ -20,7 +19,7 @@ class ProvidersFragment extends StatelessWidget { width: double.infinity, height: double.infinity, child: Consumer( - builder: (BuildContext context, ProvidersVM providersVM, Widget? child) { + builder: (BuildContext context, AppointmentsVM providersVM, Widget? child) { return Column( children: [ 16.height, @@ -37,8 +36,8 @@ class ProvidersFragment extends StatelessWidget { itemBuilder: (BuildContext context, int index) { return ProviderDetailsCard( onCardTapped: () { - if (context.read().providerCategories.isEmpty) { - context.read().getProviderCategories(); + if (context.read().branchCategories.isEmpty) { + context.read().getBranchCategories(); } navigateWithName(context, AppRoutes.bookAppointmenServicesView); }, diff --git a/lib/views/provider/branch_detail_page.dart b/lib/views/provider/branch_detail_page.dart index f9432ae..e5973cb 100644 --- a/lib/views/provider/branch_detail_page.dart +++ b/lib/views/provider/branch_detail_page.dart @@ -1,13 +1,12 @@ import 'package:car_customer_app/views/provider/sheet/items_list_sheet.dart'; +import 'package:easy_localization/easy_localization.dart'; 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/generated/locale_keys.g.dart'; -import 'package:mc_common_app/models/services/branch_model.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:mc_common_app/models/services/service_model.dart'; +import 'package:mc_common_app/models/branch_models/branch_detail_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/widgets/bottom_sheet.dart'; @@ -16,9 +15,9 @@ import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; class BranchDetailPage extends StatefulWidget { - BranchModel branchModel; + final BranchDetailModel branchDetailModel; - BranchDetailPage({required this.branchModel}); + BranchDetailPage({required this.branchDetailModel}); @override State createState() => _BranchDetailPageState(); @@ -29,7 +28,7 @@ class _BranchDetailPageState extends State { void initState() { // TODO: implement initState super.initState(); - if (widget.branchModel.branchServices!.length > 0) widget.branchModel.branchServices?.first.isExpanded = true; + if (widget.branchDetailModel.branchServices!.length > 0) widget.branchDetailModel.branchServices?.first.isExpanded = true; } @override @@ -52,7 +51,7 @@ class _BranchDetailPageState extends State { children: [ Image.asset(MyAssets.bnCar), 12.height, - "${widget.branchModel.branchName} | ${widget.branchModel.serviceProviderName}".toString().toText( + "${widget.branchDetailModel.branchName} | ${widget.branchDetailModel.serviceProviderName}".toString().toText( fontSize: 16, isBold: true, ), @@ -60,14 +59,14 @@ class _BranchDetailPageState extends State { children: [ (LocaleKeys.location.tr() + ":").toText(color: MyColors.lightTextColor, fontSize: 12), 4.width, - (widget.branchModel.distanceKm.toString() + " km").toText(fontSize: 12, isBold: true), + (widget.branchDetailModel.distanceKM.toString() + " km").toText(fontSize: 12, isBold: true), ], ), Row( children: [ ("Time" + ":").toText(color: MyColors.lightTextColor, fontSize: 12), 4.width, - "${widget.branchModel.openTime} - ${widget.branchModel.closeTime}".toText(fontSize: 12, isBold: true), + "${widget.branchDetailModel.openTime} - ${widget.branchDetailModel.closeTime}".toText(fontSize: 12, isBold: true), ], ), 2.height, @@ -123,7 +122,7 @@ class _BranchDetailPageState extends State { ) .onPress( () { - navigateWithName(context, AppRoutes.providerProfilePage, arguments: widget.branchModel.serviceProviderId); + navigateWithName(context, AppRoutes.providerProfilePage, arguments: widget.branchDetailModel.serviceProviderID); }, ), ], @@ -134,7 +133,7 @@ class _BranchDetailPageState extends State { color: MyColors.lightTextColor, isBold: true, ), - if (widget.branchModel.branchServices!.length == 0) "No Services Available".toText(fontSize: 12, isBold: true), + if (widget.branchDetailModel.branchServices!.length == 0) "No Services Available".toText(fontSize: 12, isBold: true), showServicesList(), ], ).toWhiteContainer( @@ -150,7 +149,9 @@ class _BranchDetailPageState extends State { title: "Book Appointment", maxWidth: double.infinity, margin: EdgeInsets.all(21), - onPressed: () {}, + onPressed: () { + navigateWithName(context, AppRoutes.bookAppointmenServicesView); + }, ).toContainer( paddingAll: 0, backgroundColor: Colors.white, @@ -165,7 +166,7 @@ class _BranchDetailPageState extends State { itemBuilder: (context, index) { return ExpansionTile( tilePadding: EdgeInsets.zero, - title: (widget.branchModel.branchServices![index].serviceDescription ?? "").toText( + title: (widget.branchDetailModel.branchServices![index].serviceDescription ?? "").toText( fontSize: 16, isBold: true, ), @@ -173,11 +174,11 @@ class _BranchDetailPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - showItem("Allowing home service:", (widget.branchModel.branchServices![index].isAllowAppointment ?? false) ? "Yes" : "No", valueColor: Colors.green), - showItem("Home service range", widget.branchModel.branchServices![index].customerLocationRange.toString() + "KM"), - showItem("Charges per KM", widget.branchModel.branchServices![index].customerLocationRange.toString() + "SAR"), + showItem("Allowing home service:", (widget.branchDetailModel.branchServices![index].isAllowAppointment ?? false) ? "Yes" : "No", valueColor: Colors.green), + showItem("Home service range", widget.branchDetailModel.branchServices![index].customerLocationRange.toString() + "KM"), + showItem("Charges per KM", widget.branchDetailModel.branchServices![index].customerLocationRange.toString() + "SAR"), 8.height, - (widget.branchModel.branchServices![index].itemsCount.toString() + "+ items").toText( + (widget.branchDetailModel.branchServices![index].itemsCount.toString() + "+ items").toText( fontSize: 12, isBold: true, color: MyColors.primaryColor, @@ -186,26 +187,24 @@ class _BranchDetailPageState extends State { 20.height, ], ).onPress(() { - showMyBottomSheet(context, child: ItemsListSheet(widget.branchModel.branchServices![index].providerBranchServiceId ?? 0)); + showMyBottomSheet(context, child: ItemsListSheet(widget.branchDetailModel.branchServices![index].providerBranchServiceID ?? 0)); }), ], onExpansionChanged: (value) { setState(() { - widget.branchModel.branchServices![index].isExpanded = value; + widget.branchDetailModel.branchServices![index].isExpanded = value; }); }, backgroundColor: Colors.transparent, collapsedBackgroundColor: Colors.transparent, - initiallyExpanded: widget.branchModel.branchServices![index].isExpanded, - trailing: widget.branchModel.branchServices![index].isExpanded ? Icon(Icons.keyboard_arrow_up) : Icon(Icons.keyboard_arrow_down), + initiallyExpanded: widget.branchDetailModel.branchServices![index].isExpanded!, + trailing: widget.branchDetailModel.branchServices![index].isExpanded! ? Icon(Icons.keyboard_arrow_up) : Icon(Icons.keyboard_arrow_down), ); }, separatorBuilder: (context, index) { - return Divider( - height: 1, - ); + return Divider(height: 1); }, - itemCount: widget.branchModel.branchServices!.length, + itemCount: widget.branchDetailModel.branchServices!.length, physics: NeverScrollableScrollPhysics(), shrinkWrap: true, ); diff --git a/lib/views/provider/provider_profile_page.dart b/lib/views/provider/provider_profile_page.dart index 84d041f..ac350bb 100644 --- a/lib/views/provider/provider_profile_page.dart +++ b/lib/views/provider/provider_profile_page.dart @@ -1,4 +1,4 @@ -import 'package:car_customer_app/view_models/providers_view_model.dart'; +import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; @@ -10,7 +10,7 @@ import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; class ProviderProfilePage extends StatefulWidget { - int providerId; + final int providerId; ProviderProfilePage({required this.providerId}); @@ -22,20 +22,18 @@ class _ProviderProfilePageState extends State { @override void initState() { super.initState(); - context.read().getBranchAndServices(widget.providerId ?? 0); + context.read().getBranchAndServices(widget.providerId); } @override Widget build(BuildContext context) { return Scaffold( - appBar: CustomAppBar( - title: "Provider Details", - ), + appBar: CustomAppBar(title: "Provider Details"), body: Container( width: double.infinity, height: double.infinity, padding: EdgeInsets.all(21), - child: Consumer( + child: Consumer( builder: (context, model, _) { return model.providerModel == null ? const Center(child: CircularProgressIndicator()) diff --git a/lib/views/provider/sheet/items_list_sheet.dart b/lib/views/provider/sheet/items_list_sheet.dart index 63a8fbf..7870b53 100644 --- a/lib/views/provider/sheet/items_list_sheet.dart +++ b/lib/views/provider/sheet/items_list_sheet.dart @@ -1,19 +1,14 @@ -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:car_customer_app/view_models/appointments_view_model.dart'; 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/theme/colors.dart'; -import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/widgets/empty_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; -import '../../../view_models/providers_view_model.dart'; - class ItemsListSheet extends StatefulWidget { - int serviceId; + final int serviceId; ItemsListSheet(this.serviceId); @@ -25,14 +20,14 @@ class _ItemsListSheetState extends State { @override void initState() { super.initState(); - context.read().getServiceItems(widget.serviceId ?? 0); + context.read().getServiceItems(widget.serviceId); } @override Widget build(BuildContext context) { return SizedBox( height: MediaQuery.of(context).size.height / 1.2, - child: Consumer( + child: Consumer( builder: (context, model, _) { return model.serviceItems == null ? const Center(child: CircularProgressIndicator()) diff --git a/pubspec.yaml b/pubspec.yaml index 46981f4..1c5a0d4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: cupertino_icons: ^1.0.2 mc_common_app: - path: C:/Users/mirza.shafique/AndroidStudioProjects/mc_common_app + path: D:/Development/car_common_app dev_dependencies: flutter_test: From ab723b4b976295da1a9a9d1da55a5b0b772873fc Mon Sep 17 00:00:00 2001 From: FaizHashmiCS22 Date: Wed, 21 Jun 2023 10:14:01 +0300 Subject: [PATCH 05/12] Merged the code --- lib/config/customer_routes.dart | 4 +- lib/main.dart | 9 +-- lib/repositories/provider_repo.dart | 27 ++++---- lib/view_models/appointments_view_model.dart | 61 ++++++++++++++++- lib/view_models/providers_view_model.dart | 67 ------------------- lib/views/dashboard/dashboard_page.dart | 6 +- .../fragments/branches_fragment.dart | 60 ++++++++--------- .../dashboard/fragments/home_fragment.dart | 3 +- .../fragments/providers_fragment.dart | 9 ++- lib/views/provider/branch_detail_page.dart | 45 +++++++------ lib/views/provider/provider_profile_page.dart | 10 +-- .../provider/sheet/items_list_sheet.dart | 13 ++-- pubspec.yaml | 2 +- 13 files changed, 144 insertions(+), 172 deletions(-) delete mode 100644 lib/view_models/providers_view_model.dart diff --git a/lib/config/customer_routes.dart b/lib/config/customer_routes.dart index f68dfef..7c9b98a 100644 --- a/lib/config/customer_routes.dart +++ b/lib/config/customer_routes.dart @@ -8,7 +8,7 @@ import 'package:flutter/cupertino.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; -import 'package:mc_common_app/models/services/branch_model.dart'; +import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; import 'package:mc_common_app/views/advertisement/ads_detail_view.dart'; import 'package:mc_common_app/views/advertisement/create_ad_view.dart'; import 'package:mc_common_app/views/payments/payment_methods_view.dart'; @@ -22,7 +22,7 @@ class CustomerAppRoutes { AppRoutes.createAdView: (context) => CreateAdView(), AppRoutes.bookAppointmenServicesView: (context) => BookAppointmentServicesView(), AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(), - AppRoutes.branchDetailPage: (context) => BranchDetailPage(branchModel: ModalRoute.of(context)!.settings.arguments as BranchModel), + 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 e053104..b3bd097 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,6 @@ import 'package:car_customer_app/config/customer_routes.dart'; import 'package:car_customer_app/repositories/provider_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:car_customer_app/view_models/providers_view_model.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; @@ -53,15 +52,9 @@ Future main() async { adsRepo: injector.get(), ), ), - ChangeNotifierProvider( - create: (_) => ProvidersVM( - commonServices: injector.get(), - commonRepo: injector.get(), - providerRepo: injector.get(), - ), - ), ChangeNotifierProvider( create: (_) => AppointmentsVM( + providerRepo: injector.get(), commonServices: injector.get(), commonRepo: injector.get(), ), diff --git a/lib/repositories/provider_repo.dart b/lib/repositories/provider_repo.dart index d92a66d..c354ace 100644 --- a/lib/repositories/provider_repo.dart +++ b/lib/repositories/provider_repo.dart @@ -1,21 +1,17 @@ import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'package:mc_common_app/models/model/provider_model.dart'; -import 'package:mc_common_app/models/services/item_model.dart'; -import 'package:mc_common_app/models/services/near_branch_model.dart'; -import 'package:http/http.dart' as http; +import 'package:flutter/cupertino.dart'; 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:flutter/cupertino.dart'; +import 'package:mc_common_app/models/generic_resp_model.dart'; +import 'package:mc_common_app/models/model/provider_model.dart'; +import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; +import 'package:mc_common_app/models/services/item_model.dart'; abstract class ProviderRepo { - Future getAllNearBranchAndServices(); + Future> getAllNearBranchAndServices(); Future getServiceItems(int serviceId); @@ -23,11 +19,14 @@ abstract class ProviderRepo { } class ProviderRepoImp implements ProviderRepo { + ApiClient apiClient = injector.get(); + AppState appState = injector.get(); + @override - Future getAllNearBranchAndServices() async { - // var postParams = {"serviceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? ""}; - String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().getJsonForObject((json) => NearBrancheModel.fromJson(json), ApiConsts.GetAllNearBranches, token: t); + Future> getAllNearBranchAndServices() async { + GenericRespModel adsGenericModel = await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.GetAllNearBranches, token: appState.getUser.data!.accessToken); + List nearBranches = List.generate(adsGenericModel.data.length, (index) => BranchDetailModel.fromJson(adsGenericModel.data[index])); + return nearBranches; } @override diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 2fb9aa2..6371923 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -1,16 +1,22 @@ -import 'package:flutter/cupertino.dart'; +import 'package:car_customer_app/repositories/provider_repo.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; +import 'package:mc_common_app/models/model/provider_model.dart'; +import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; import 'package:mc_common_app/models/provider_category_model.dart'; import 'package:mc_common_app/models/provider_service_model.dart'; +import 'package:mc_common_app/models/services/item_model.dart'; import 'package:mc_common_app/models/widgets_models.dart'; import 'package:mc_common_app/repositories/common_repo.dart'; import 'package:mc_common_app/services/common_services.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/view_models/base_view_model.dart'; -class AppointmentsVM extends ChangeNotifier { +class AppointmentsVM extends BaseVM { final CommonRepo commonRepo; final CommonAppServices commonServices; + final ProviderRepo providerRepo; - AppointmentsVM({required this.commonServices, required this.commonRepo}); + AppointmentsVM({required this.commonServices, required this.providerRepo, required this.commonRepo}); bool isFetchingLists = false; @@ -99,4 +105,53 @@ class AppointmentsVM extends ChangeNotifier { isFetchingLists = false; notifyListeners(); } + + List providersFilterOptions = []; + List nearbyBranches = []; + ItemModel? serviceItems; + ProviderModel? providerModel; + + populateProvidersFilterList() { + providersFilterOptions.clear(); + providersFilterOptions = [ + FilterListModel(title: "All Providers", isSelected: true, id: -1), + FilterListModel(title: "Maintenance", isSelected: false, id: 0), + FilterListModel(title: "Oil Service", isSelected: false, id: 1), + FilterListModel(title: "Accessories", isSelected: false, id: 2), + FilterListModel(title: "Tire Service", isSelected: false, id: 3), + FilterListModel(title: "Dent and Paint", isSelected: false, id: 4), + ]; + notifyListeners(); + } + + applyFilterOnProviders({required int index}) { + if (providersFilterOptions.isEmpty) return; + for (var value in providersFilterOptions) { + value.isSelected = false; + } + providersFilterOptions[index].isSelected = true; + notifyListeners(); + } + + //Create new branch + 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 { + serviceItems = null; + serviceItems = await providerRepo.getServiceItems(serviceId); + setState(ViewState.idle); + return serviceItems; + } + + getBranchAndServices(int providerId) async { + providerModel = null; + providerModel = await providerRepo.getBranchAndServices(providerId); + setState(ViewState.idle); + } } diff --git a/lib/view_models/providers_view_model.dart b/lib/view_models/providers_view_model.dart deleted file mode 100644 index 568f713..0000000 --- a/lib/view_models/providers_view_model.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:mc_common_app/models/model/provider_model.dart'; -import 'package:mc_common_app/models/services/item_model.dart'; -import 'package:mc_common_app/models/services/near_branch_model.dart'; -import 'package:car_customer_app/repositories/provider_repo.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:mc_common_app/models/widgets_models.dart'; -import 'package:mc_common_app/repositories/common_repo.dart'; -import 'package:mc_common_app/services/common_services.dart'; -import 'package:mc_common_app/utils/enums.dart'; -import 'package:mc_common_app/view_models/base_view_model.dart'; - -class ProvidersVM extends BaseVM { - final CommonRepo commonRepo; - final CommonAppServices commonServices; - final ProviderRepo providerRepo; - - ProvidersVM({required this.commonServices, required this.commonRepo, required this.providerRepo}); - - List providersFilterOptions = []; - NearBrancheModel? branches; - ItemModel? serviceItems; - ProviderModel? providerModel; - - populateProvidersFilterList() { - providersFilterOptions.clear(); - providersFilterOptions = [ - FilterListModel(title: "All Providers", isSelected: true, id: -1), - FilterListModel(title: "Maintenance", isSelected: false, id: 0), - FilterListModel(title: "Oil Service", isSelected: false, id: 1), - FilterListModel(title: "Accessories", isSelected: false, id: 2), - FilterListModel(title: "Tire Service", isSelected: false, id: 3), - FilterListModel(title: "Dent and Paint", isSelected: false, id: 4), - ]; - notifyListeners(); - } - - applyFilterOnProviders({required int index}) { - if (providersFilterOptions.isEmpty) return; - for (var value in providersFilterOptions) { - value.isSelected = false; - } - providersFilterOptions[index].isSelected = true; - notifyListeners(); - } - - //Create new branch - getAllNearBranches({bool isNeedToRebuild = false}) async { - //TODO: needs to lat,long into API - branches = null; - if (isNeedToRebuild) setState(ViewState.busy); - branches = await providerRepo.getAllNearBranchAndServices(); - setState(ViewState.idle); - } - - Future getServiceItems(int serviceId) async { - serviceItems = null; - serviceItems = await providerRepo.getServiceItems(serviceId); - setState(ViewState.idle); - return serviceItems; - } - - getBranchAndServices(int providerId) async { - providerModel = null; - providerModel = await providerRepo.getBranchAndServices(providerId); - setState(ViewState.idle); - } -} diff --git a/lib/views/dashboard/dashboard_page.dart b/lib/views/dashboard/dashboard_page.dart index 1c9fa86..abae722 100644 --- a/lib/views/dashboard/dashboard_page.dart +++ b/lib/views/dashboard/dashboard_page.dart @@ -2,11 +2,9 @@ import 'dart:async'; import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:car_customer_app/view_models/dashboard_view_model.dart'; -import 'package:car_customer_app/view_models/providers_view_model.dart'; import 'package:car_customer_app/views/dashboard/fragments/ads_fragment.dart'; import 'package:car_customer_app/views/dashboard/fragments/appointments_fragment.dart'; import 'package:car_customer_app/views/dashboard/fragments/home_fragment.dart'; -import 'package:car_customer_app/views/dashboard/fragments/providers_fragment.dart'; import 'package:car_customer_app/views/dashboard/fragments/settings_fragment.dart'; import 'package:car_customer_app/views/dashboard/widgets/bottom_nav_bar.dart'; import 'package:car_customer_app/views/dashboard/widgets/drawer_widget.dart'; @@ -36,9 +34,9 @@ class _DashboardPageState extends State { fetchUsername(); scheduleMicrotask(() { context.read().populateAppointmentsFilterList(); - context.read().populateProvidersFilterList(); + context.read().populateProvidersFilterList(); context.read().populateAdsFilterList(); - context.read().getAllNearBranches(); + context.read().getAllNearBranches(); _onRefresh(); }); } diff --git a/lib/views/dashboard/fragments/branches_fragment.dart b/lib/views/dashboard/fragments/branches_fragment.dart index d0ab34c..bca8f61 100644 --- a/lib/views/dashboard/fragments/branches_fragment.dart +++ b/lib/views/dashboard/fragments/branches_fragment.dart @@ -1,17 +1,18 @@ -import 'package:car_customer_app/view_models/providers_view_model.dart'; +import 'package:car_customer_app/view_models/appointments_view_model.dart'; +import 'package:easy_localization/easy_localization.dart'; 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/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/provider_branches_models/branch_detail_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/widgets/common_widgets/categories_list.dart'; import 'package:mc_common_app/widgets/common_widgets/provider_details_card.dart'; import 'package:provider/provider.dart'; -import 'package:easy_localization/easy_localization.dart'; class BranchesFragment extends StatelessWidget { const BranchesFragment({Key? key}) : super(key: key); @@ -23,53 +24,52 @@ class BranchesFragment extends StatelessWidget { width: double.infinity, height: double.infinity, child: Consumer( - builder: (BuildContext context, ProvidersVM providersVM, Widget? child) { + builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) { return Column( children: [ 16.height, FiltersList( - filterList: providersVM.providersFilterOptions, - onFilterTapped: (index, selectedFilterId) => providersVM.applyFilterOnProviders(index: index), + filterList: appointmentsVM.providersFilterOptions, + onFilterTapped: (index, selectedFilterId) => appointmentsVM.applyFilterOnProviders(index: index), ), 16.height, Expanded( child: RefreshIndicator( onRefresh: () async { - context.read().getAllNearBranches(isNeedToRebuild: true); + context.read().getAllNearBranches(isNeedToRebuild: true); }, child: SingleChildScrollView( child: Container( width: double.infinity, height: MediaQuery.of(context).size.height / 1.37, - child: Consumer( + child: Consumer( builder: (context, model, _) { if (model.state == ViewState.busy) { return const Center(child: CircularProgressIndicator()); } else { - return model.branches?.data == null - ? const Center(child: Text("No Branch Found")) - : model.branches!.data!.isEmpty - ? Center(child: Text(LocaleKeys.no_branch.tr())) - : ListView.separated( - itemBuilder: (context, index) { - return ProviderDetailsCard( - onCardTapped: () { - navigateWithName(context, AppRoutes.branchDetailPage, arguments: model.branches!.data![index]); - }, - providerImageUrl: MyAssets.bnCar, - title: model.branches!.data![index].branchName ?? "", - providerLocation: model.branches!.data![index].distanceKm.toString() + " KM", - providerName: model.branches!.data![index].serviceProviderName ?? "", - providerRatings: "4.9", - items: model.branches!.data![index].branchServices, - ); - }, - separatorBuilder: (context, index) { - return 12.height; + return model.nearbyBranches.isEmpty + ? Center(child: LocaleKeys.no_branch.tr().toText(fontSize: 16, color: MyColors.lightTextColor)) + : ListView.separated( + itemCount: model.nearbyBranches.length, + itemBuilder: (context, index) { + BranchDetailModel branchDetailModel = model.nearbyBranches[index]; + return ProviderDetailsCard( + onCardTapped: () { + navigateWithName(context, AppRoutes.branchDetailPage, arguments: branchDetailModel); }, - itemCount: model.branches!.data!.length, - padding: const EdgeInsets.all(12), + providerImageUrl: MyAssets.bnCar, + title: branchDetailModel.branchName ?? "", + providerLocation: branchDetailModel.distanceKm.toString() + " KM", + providerName: branchDetailModel.serviceProviderName ?? "", + providerRatings: "4.9", + items: branchDetailModel.branchServices, ); + }, + separatorBuilder: (context, index) { + return 12.height; + }, + padding: const EdgeInsets.all(12), + ); } }, ), diff --git a/lib/views/dashboard/fragments/home_fragment.dart b/lib/views/dashboard/fragments/home_fragment.dart index c7c4d6b..9d10167 100644 --- a/lib/views/dashboard/fragments/home_fragment.dart +++ b/lib/views/dashboard/fragments/home_fragment.dart @@ -1,6 +1,5 @@ import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:car_customer_app/view_models/dashboard_view_model.dart'; -import 'package:car_customer_app/view_models/providers_view_model.dart'; import 'package:car_customer_app/views/appointments/widgets/customer_appointment_slider_widget.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; @@ -43,7 +42,7 @@ class HomeFragment extends StatelessWidget { onSubtitleTapped: () { print("value: ${AppState().getUser.data!.accessToken}"); context.read().onNavbarTapped(0); - context.read().applyFilterOnProviders(index: 0); + context.read().applyFilterOnProviders(index: 0); }).horPaddingMain(), const ServiceProviderWidget().horPaddingMain(), Consumer( diff --git a/lib/views/dashboard/fragments/providers_fragment.dart b/lib/views/dashboard/fragments/providers_fragment.dart index 09e6dab..9419afd 100644 --- a/lib/views/dashboard/fragments/providers_fragment.dart +++ b/lib/views/dashboard/fragments/providers_fragment.dart @@ -1,5 +1,4 @@ import 'package:car_customer_app/view_models/appointments_view_model.dart'; -import 'package:car_customer_app/view_models/providers_view_model.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; @@ -20,13 +19,13 @@ class ProvidersFragment extends StatelessWidget { width: double.infinity, height: double.infinity, child: Consumer( - builder: (BuildContext context, ProvidersVM providersVM, Widget? child) { + builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) { return Column( children: [ 16.height, FiltersList( - filterList: providersVM.providersFilterOptions, - onFilterTapped: (index, selectedFilterId) => providersVM.applyFilterOnProviders(index: index), + filterList: appointmentsVM.providersFilterOptions, + onFilterTapped: (index, selectedFilterId) => appointmentsVM.applyFilterOnProviders(index: index), ), 16.height, Expanded( @@ -43,7 +42,7 @@ class ProvidersFragment extends StatelessWidget { navigateWithName(context, AppRoutes.bookAppointmenServicesView); }, providerImageUrl: MyAssets.bnCar, - title:"", + title: "", providerLocation: " 3km", providerName: "Al Ahmed Maintenance", providerRatings: "4.9", diff --git a/lib/views/provider/branch_detail_page.dart b/lib/views/provider/branch_detail_page.dart index dbace34..654e6e1 100644 --- a/lib/views/provider/branch_detail_page.dart +++ b/lib/views/provider/branch_detail_page.dart @@ -1,13 +1,12 @@ import 'package:car_customer_app/views/provider/sheet/items_list_sheet.dart'; +import 'package:easy_localization/easy_localization.dart'; 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/generated/locale_keys.g.dart'; -import 'package:mc_common_app/models/services/branch_model.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:mc_common_app/models/services/service_model.dart'; +import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/widgets/bottom_sheet.dart'; @@ -16,9 +15,9 @@ import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; class BranchDetailPage extends StatefulWidget { - BranchModel branchModel; + final BranchDetailModel branchDetailModel; - BranchDetailPage({required this.branchModel}); + BranchDetailPage({required this.branchDetailModel}); @override State createState() => _BranchDetailPageState(); @@ -29,7 +28,7 @@ class _BranchDetailPageState extends State { void initState() { // TODO: implement initState super.initState(); - if (widget.branchModel.branchServices!.length > 0) widget.branchModel.branchServices?.first.isExpanded = true; + if (widget.branchDetailModel.branchServices!.length > 0) widget.branchDetailModel.branchServices?.first.isExpanded = true; } @override @@ -52,7 +51,7 @@ class _BranchDetailPageState extends State { children: [ Image.asset(MyAssets.bnCar), 12.height, - "${widget.branchModel.branchName} | ${widget.branchModel.serviceProviderName}".toString().toText( + "${widget.branchDetailModel.branchName} | ${widget.branchDetailModel.serviceProviderName}".toString().toText( fontSize: 16, isBold: true, ), @@ -60,14 +59,14 @@ class _BranchDetailPageState extends State { children: [ (LocaleKeys.location.tr() + ":").toText(color: MyColors.lightTextColor, fontSize: 12), 4.width, - (widget.branchModel.distanceKm.toString() + " km").toText(fontSize: 12, isBold: true), + (widget.branchDetailModel.distanceKm.toString() + " km").toText(fontSize: 12, isBold: true), ], ), Row( children: [ ("Time" + ":").toText(color: MyColors.lightTextColor, fontSize: 12), 4.width, - "${widget.branchModel.openTime} - ${widget.branchModel.closeTime}".toText(fontSize: 12, isBold: true), + "${widget.branchDetailModel.openTime} - ${widget.branchDetailModel.closeTime}".toText(fontSize: 12, isBold: true), ], ), 2.height, @@ -123,7 +122,7 @@ class _BranchDetailPageState extends State { ) .onPress( () { - navigateWithName(context, AppRoutes.providerProfilePage, arguments: widget.branchModel.serviceProviderId); + navigateWithName(context, AppRoutes.providerProfilePage, arguments: widget.branchDetailModel.serviceProviderId); }, ), ], @@ -134,7 +133,7 @@ class _BranchDetailPageState extends State { color: MyColors.lightTextColor, isBold: true, ), - if (widget.branchModel.branchServices!.length == 0) "No Services Available".toText(fontSize: 12, isBold: true), + if (widget.branchDetailModel.branchServices!.length == 0) "No Services Available".toText(fontSize: 12, isBold: true), showServicesList(), ], ).toWhiteContainer( @@ -150,7 +149,9 @@ class _BranchDetailPageState extends State { title: "Book Appointment", maxWidth: double.infinity, margin: EdgeInsets.all(21), - onPressed: () {}, + onPressed: () { + navigateWithName(context, AppRoutes.bookAppointmenServicesView); + }, ).toContainer( paddingAll: 0, backgroundColor: Colors.white, @@ -165,7 +166,7 @@ class _BranchDetailPageState extends State { itemBuilder: (context, index) { return ExpansionTile( tilePadding: EdgeInsets.zero, - title: (widget.branchModel.branchServices![index].serviceDescription ?? "").toText( + title: (widget.branchDetailModel.branchServices![index].serviceDescription ?? "").toText( fontSize: 16, isBold: true, ), @@ -173,11 +174,11 @@ class _BranchDetailPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - showItem("Allowing home service:", (widget.branchModel.branchServices![index].isAllowAppointment ?? false) ? "Yes" : "No", valueColor: Colors.green), - showItem("Home service range", widget.branchModel.branchServices![index].customerLocationRange.toString() + "KM"), - showItem("Charges per KM", widget.branchModel.branchServices![index].customerLocationRange.toString() + "SAR"), + showItem("Allowing home service:", (widget.branchDetailModel.branchServices![index].isAllowAppointment ?? false) ? "Yes" : "No", valueColor: Colors.green), + showItem("Home service range", widget.branchDetailModel.branchServices![index].customerLocationRange.toString() + "KM"), + showItem("Charges per KM", widget.branchDetailModel.branchServices![index].customerLocationRange.toString() + "SAR"), 8.height, - (widget.branchModel.branchServices![index].itemsCount.toString() + "+ items").toText( + (widget.branchDetailModel.branchServices![index].itemsCount.toString() + "+ items").toText( fontSize: 12, isBold: true, color: MyColors.primaryColor, @@ -186,18 +187,18 @@ class _BranchDetailPageState extends State { 20.height, ], ).onPress(() { - showMyBottomSheet(context, child: ItemsListSheet(widget.branchModel.branchServices![index].serviceProviderServiceId ?? 0)); + showMyBottomSheet(context, child: ItemsListSheet(widget.branchDetailModel.branchServices![index].serviceProviderServiceId ?? 0)); }), ], onExpansionChanged: (value) { setState(() { - widget.branchModel.branchServices![index].isExpanded = value; + widget.branchDetailModel.branchServices![index].isExpanded = value; }); }, backgroundColor: Colors.transparent, collapsedBackgroundColor: Colors.transparent, - initiallyExpanded: widget.branchModel.branchServices![index].isExpanded, - trailing: widget.branchModel.branchServices![index].isExpanded ? Icon(Icons.keyboard_arrow_up) : Icon(Icons.keyboard_arrow_down), + initiallyExpanded: widget.branchDetailModel.branchServices![index].isExpanded, + trailing: widget.branchDetailModel.branchServices![index].isExpanded ? Icon(Icons.keyboard_arrow_up) : Icon(Icons.keyboard_arrow_down), ); }, separatorBuilder: (context, index) { @@ -205,7 +206,7 @@ class _BranchDetailPageState extends State { height: 1, ); }, - itemCount: widget.branchModel.branchServices!.length, + itemCount: widget.branchDetailModel.branchServices!.length, physics: NeverScrollableScrollPhysics(), shrinkWrap: true, ); diff --git a/lib/views/provider/provider_profile_page.dart b/lib/views/provider/provider_profile_page.dart index a16f39f..e895682 100644 --- a/lib/views/provider/provider_profile_page.dart +++ b/lib/views/provider/provider_profile_page.dart @@ -1,4 +1,5 @@ -import 'package:car_customer_app/view_models/providers_view_model.dart'; +import 'package:car_customer_app/view_models/appointments_view_model.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; @@ -12,10 +13,9 @@ import 'package:mc_common_app/widgets/common_widgets/provider_details_card.dart' import 'package:mc_common_app/widgets/empty_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; -import 'package:easy_localization/easy_localization.dart'; class ProviderProfilePage extends StatefulWidget { - int providerId; + final int providerId; ProviderProfilePage({required this.providerId}); @@ -27,7 +27,7 @@ class _ProviderProfilePageState extends State { @override void initState() { super.initState(); - context.read().getBranchAndServices(widget.providerId ?? 0); + context.read().getBranchAndServices(widget.providerId ?? 0); } @override @@ -40,7 +40,7 @@ class _ProviderProfilePageState extends State { width: double.infinity, height: double.infinity, padding: EdgeInsets.all(21), - child: Consumer( + child: Consumer( builder: (context, model, _) { return model.providerModel == null ? const Center(child: CircularProgressIndicator()) diff --git a/lib/views/provider/sheet/items_list_sheet.dart b/lib/views/provider/sheet/items_list_sheet.dart index 63a8fbf..7870b53 100644 --- a/lib/views/provider/sheet/items_list_sheet.dart +++ b/lib/views/provider/sheet/items_list_sheet.dart @@ -1,19 +1,14 @@ -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:car_customer_app/view_models/appointments_view_model.dart'; 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/theme/colors.dart'; -import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/widgets/empty_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; -import '../../../view_models/providers_view_model.dart'; - class ItemsListSheet extends StatefulWidget { - int serviceId; + final int serviceId; ItemsListSheet(this.serviceId); @@ -25,14 +20,14 @@ class _ItemsListSheetState extends State { @override void initState() { super.initState(); - context.read().getServiceItems(widget.serviceId ?? 0); + context.read().getServiceItems(widget.serviceId); } @override Widget build(BuildContext context) { return SizedBox( height: MediaQuery.of(context).size.height / 1.2, - child: Consumer( + child: Consumer( builder: (context, model, _) { return model.serviceItems == null ? const Center(child: CircularProgressIndicator()) diff --git a/pubspec.yaml b/pubspec.yaml index 46981f4..c845519 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: cupertino_icons: ^1.0.2 mc_common_app: - path: C:/Users/mirza.shafique/AndroidStudioProjects/mc_common_app + path: D:/Development/car_common_app/ dev_dependencies: flutter_test: From f0130d189a20cd4d772bc77e741618ca534332e4 Mon Sep 17 00:00:00 2001 From: FaizHashmiCS22 Date: Wed, 21 Jun 2023 11:05:23 +0300 Subject: [PATCH 06/12] Merged the code --- lib/repositories/provider_repo.dart | 18 +++++++------- lib/view_models/appointments_view_model.dart | 8 +++---- lib/views/provider/provider_profile_page.dart | 24 +++++++++---------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/repositories/provider_repo.dart b/lib/repositories/provider_repo.dart index c354ace..3a6804a 100644 --- a/lib/repositories/provider_repo.dart +++ b/lib/repositories/provider_repo.dart @@ -6,8 +6,8 @@ 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/model/provider_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/services/item_model.dart'; abstract class ProviderRepo { @@ -15,7 +15,7 @@ abstract class ProviderRepo { Future getServiceItems(int serviceId); - Future getBranchAndServices(int providerId); + Future getBranchAndServices(int providerId); } class ProviderRepoImp implements ProviderRepo { @@ -34,17 +34,17 @@ class ProviderRepoImp implements ProviderRepo { var queryParameters = { "ServiceProviderServiceID": serviceId.toString(), }; - String? token = AppState().getUser.data?.accessToken; + String? token = appState.getUser.data?.accessToken; debugPrint(token); - return await injector - .get() - .getJsonForObject((json) => ItemModel.fromJson(json), ApiConsts.getServiceItems, queryParameters: queryParameters, token: AppState().getUser.data!.accessToken ?? ""); + return await apiClient.getJsonForObject((json) => ItemModel.fromJson(json), ApiConsts.getServiceItems, queryParameters: queryParameters, token: AppState().getUser.data!.accessToken ?? ""); } @override - Future getBranchAndServices(int providerId) async { + Future getBranchAndServices(int providerId) async { var postParams = {"serviceProviderID": providerId.toString()}; - String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().getJsonForObject((json) => ProviderModel.fromJson(json), ApiConsts.BranchesAndServices, queryParameters: postParams, token: t); + GenericRespModel adsGenericModel = + await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.BranchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams); + + return ProviderProfileModel.fromJson(adsGenericModel.data); } } diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 6371923..fc4daae 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -1,7 +1,7 @@ import 'package:car_customer_app/repositories/provider_repo.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; -import 'package:mc_common_app/models/model/provider_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/provider_category_model.dart'; import 'package:mc_common_app/models/provider_service_model.dart'; import 'package:mc_common_app/models/services/item_model.dart'; @@ -109,7 +109,7 @@ class AppointmentsVM extends BaseVM { List providersFilterOptions = []; List nearbyBranches = []; ItemModel? serviceItems; - ProviderModel? providerModel; + ProviderProfileModel? providerProfileModel; populateProvidersFilterList() { providersFilterOptions.clear(); @@ -150,8 +150,8 @@ class AppointmentsVM extends BaseVM { } getBranchAndServices(int providerId) async { - providerModel = null; - providerModel = await providerRepo.getBranchAndServices(providerId); + providerProfileModel = null; + providerProfileModel = await providerRepo.getBranchAndServices(providerId); setState(ViewState.idle); } } diff --git a/lib/views/provider/provider_profile_page.dart b/lib/views/provider/provider_profile_page.dart index e895682..8b411ac 100644 --- a/lib/views/provider/provider_profile_page.dart +++ b/lib/views/provider/provider_profile_page.dart @@ -27,7 +27,7 @@ class _ProviderProfilePageState extends State { @override void initState() { super.initState(); - context.read().getBranchAndServices(widget.providerId ?? 0); + context.read().getBranchAndServices(widget.providerId); } @override @@ -42,9 +42,9 @@ class _ProviderProfilePageState extends State { padding: EdgeInsets.all(21), child: Consumer( builder: (context, model, _) { - return model.providerModel == null + return model.providerProfileModel == null ? const Center(child: CircularProgressIndicator()) - : model.providerModel!.data == null + : model.providerProfileModel == null ? const EmptyWidget() : SingleChildScrollView( child: Column( @@ -55,7 +55,7 @@ class _ProviderProfilePageState extends State { //TODO: company logo/banner not added form provider app yet Image.asset(MyAssets.bnCar), 12.height, - model.providerModel!.data!.companyName.toString().toText( + model.providerProfileModel!.companyName.toString().toText( fontSize: 16, isBold: true, ), @@ -77,28 +77,28 @@ class _ProviderProfilePageState extends State { allPading: 12, ), 12.height, - model.providerModel!.data!.serviceProviderBranch == null + model.providerProfileModel!.serviceProviderBranch == null ? const Center(child: Text("No Branch Found")) - : model.providerModel!.data!.serviceProviderBranch!.isEmpty + : model.providerProfileModel!.serviceProviderBranch!.isEmpty ? Center(child: Text(LocaleKeys.no_branch.tr())) : ListView.separated( itemBuilder: (context, index) { return ProviderDetailsCard( onCardTapped: () { - navigateWithName(context, AppRoutes.branchDetailPage, arguments: model.providerModel!.data!.serviceProviderBranch![index]); + navigateWithName(context, AppRoutes.branchDetailPage, arguments: model.providerProfileModel!.serviceProviderBranch![index]); }, providerImageUrl: MyAssets.bnCar, - title: model.providerModel!.data!.serviceProviderBranch![index].branchName ?? "", - providerLocation: model.providerModel!.data!.serviceProviderBranch![index].distanceKm.toString() + " KM", - providerName: model.providerModel!.data!.serviceProviderBranch![index].serviceProviderName ?? "", + title: model.providerProfileModel!.serviceProviderBranch![index].branchName ?? "", + providerLocation: model.providerProfileModel!.serviceProviderBranch![index].distanceKm.toString() + " KM", + providerName: model.providerProfileModel!.serviceProviderBranch![index].serviceProviderName ?? "", providerRatings: "4.9", - items: model.providerModel!.data!.serviceProviderBranch![index].branchServices, + items: model.providerProfileModel!.serviceProviderBranch![index].branchServices, ); }, separatorBuilder: (context, index) { return 12.height; }, - itemCount: model.providerModel!.data!.serviceProviderBranch!.length, + itemCount: model.providerProfileModel!.serviceProviderBranch!.length, physics: NeverScrollableScrollPhysics(), shrinkWrap: true, ) From 436befd111513ffcafc0961acec758928624a142 Mon Sep 17 00:00:00 2001 From: FaizHashmiCS22 Date: Wed, 21 Jun 2023 15:26:37 +0300 Subject: [PATCH 07/12] Updated the MOdels --- lib/config/customer_dependencies.dart | 2 +- lib/main.dart | 1 - lib/repositories/provider_repo.dart | 17 ++- lib/view_models/appointments_view_model.dart | 95 +++++++++++--- ...rt => book_appointment_services_view.dart} | 4 +- ...appointment_service_pick_bottom_sheet.dart | 25 ++-- .../fragments/providers_fragment.dart | 120 +++++++++--------- .../provider/sheet/items_list_sheet.dart | 108 ++++++++-------- 8 files changed, 211 insertions(+), 161 deletions(-) rename lib/views/appointments/{appointment_services_list_view.dart => book_appointment_services_view.dart} (98%) diff --git a/lib/config/customer_dependencies.dart b/lib/config/customer_dependencies.dart index b41d76a..3debdff 100644 --- a/lib/config/customer_dependencies.dart +++ b/lib/config/customer_dependencies.dart @@ -4,7 +4,7 @@ import 'package:mc_common_app/config/dependencies.dart'; class CustomerDependencies { static void addDependencies() { AppDependencies.addDependencies(); - injector.registerSingleton(() => BranchRepoImp()); + injector.registerSingleton(() => ProviderRepoImp()); } } diff --git a/lib/main.dart b/lib/main.dart index 87a905d..a2cfeff 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -58,7 +58,6 @@ Future main() async { create: (_) => AppointmentsVM( providerRepo: injector.get(), commonServices: injector.get(), - branchRepo: injector.get(), commonRepo: injector.get(), ), ), diff --git a/lib/repositories/provider_repo.dart b/lib/repositories/provider_repo.dart index 3a6804a..702a764 100644 --- a/lib/repositories/provider_repo.dart +++ b/lib/repositories/provider_repo.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:flutter/cupertino.dart'; 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'; @@ -13,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); } @@ -30,13 +29,19 @@ class ProviderRepoImp implements ProviderRepo { } @override - Future getServiceItems(int serviceId) async { + Future> getServiceItems(int serviceId) async { var queryParameters = { "ServiceProviderServiceID": serviceId.toString(), }; - String? token = appState.getUser.data?.accessToken; - debugPrint(token); - return await apiClient.getJsonForObject((json) => ItemModel.fromJson(json), ApiConsts.getServiceItems, queryParameters: queryParameters, token: AppState().getUser.data!.accessToken ?? ""); + + GenericRespModel adsGenericModel = await apiClient.getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.getServiceItems, + token: appState.getUser.data!.accessToken, + queryParameters: queryParameters, + ); + List serviceItems = List.generate(adsGenericModel.data.length, (index) => ServiceItemModel.fromJson(adsGenericModel.data[index])); + return serviceItems; } @override diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index fc4daae..a65a99c 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -1,15 +1,16 @@ import 'package:car_customer_app/repositories/provider_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/provider_category_model.dart'; -import 'package:mc_common_app/models/provider_service_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/repositories/common_repo.dart'; import 'package:mc_common_app/services/common_services.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/view_models/base_view_model.dart'; +import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; class AppointmentsVM extends BaseVM { final CommonRepo commonRepo; @@ -25,7 +26,7 @@ class AppointmentsVM extends BaseVM { bool isFetchingServices = false; - List providerCategories = []; + List branchCategories = []; bool isHomeTapped = false; @@ -41,37 +42,31 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - SelectionModel providerCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateProviderCategoryId(SelectionModel id) async { - providerCategoryId = id; + branchSelectedCategoryId = id; await getProviderServices(id.selectedId); notifyListeners(); } - List providerServices = []; + List branchServices = []; - SelectionModel providerServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - void updateProviderServiceId(SelectionModel id) async { - providerServiceId = id; - notifyListeners(); - } - - void getProviderCategories() async { - notifyListeners(); - providerCategories = await commonRepo.getProviderServiceCategories(); + void updateBranchServiceId(SelectionModel id) async { + branchSelectedServiceId = id; notifyListeners(); } getProviderServices(int categoryId) async { - providerServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); isHomeTapped = false; pickedHomeLocation = ""; if (categoryId != -1) { isFetchingServices = true; notifyListeners(); - providerServices = await commonRepo.getProviderServices(categoryId: categoryId); + // branchServices = await commonRepo.getProviderServices(categoryId: categoryId); isFetchingServices = false; notifyListeners(); @@ -108,7 +103,7 @@ class AppointmentsVM extends BaseVM { List providersFilterOptions = []; List nearbyBranches = []; - ItemModel? serviceItems; + List serviceItems = []; ProviderProfileModel? providerProfileModel; populateProvidersFilterList() { @@ -142,8 +137,8 @@ class AppointmentsVM extends BaseVM { setState(ViewState.idle); } - Future getServiceItems(int serviceId) async { - serviceItems = null; + Future> getServiceItems(int serviceId) async { + serviceItems.clear(); serviceItems = await providerRepo.getServiceItems(serviceId); setState(ViewState.idle); return serviceItems; @@ -154,4 +149,64 @@ class AppointmentsVM extends BaseVM { providerProfileModel = await providerRepo.getBranchAndServices(providerId); setState(ViewState.idle); } + + String pickHomeLocationError = ""; + + SelectionModel branchServiceId = 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 branchServices) { + if (!isCategoryAlreadyPresent(value.categoryId!)) { + branchCategories.add(DropValue(value.categoryId!, value.categoryName!, "")); + } + } + notifyListeners(); + } + + getBranchServices(int categoryId) async { + branchServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + isHomeTapped = false; + pickedHomeLocation = ""; + pickHomeLocationError = ""; + if (categoryId != -1) { + isFetchingServices = true; + notifyListeners(); + branchServices = getFilteredBranchServices(categoryId: categoryId, branchId: 6); + isFetchingServices = false; + + notifyListeners(); + } + } + + List getFilteredBranchServices({required int branchId, required int categoryId}) { + // List filteredServices = nearbyBranchesList.where((element) => element) + return []; + } + + void updatePickHomeLocationError(String value) { + pickHomeLocationError = value; + notifyListeners(); + } + + bool isServiceSelectionValidated() { + if (branchServiceId.selectedId == -1) { + return false; + } + + if (isHomeTapped) { + if (pickedHomeLocation == "") { + updatePickHomeLocationError(GlobalConsts.homeLocationEmptyError); + return false; + } + } + return true; + } } diff --git a/lib/views/appointments/appointment_services_list_view.dart b/lib/views/appointments/book_appointment_services_view.dart similarity index 98% rename from lib/views/appointments/appointment_services_list_view.dart rename to lib/views/appointments/book_appointment_services_view.dart index 3d6355c..ed6f6f0 100644 --- a/lib/views/appointments/appointment_services_list_view.dart +++ b/lib/views/appointments/book_appointment_services_view.dart @@ -16,8 +16,8 @@ import 'package:mc_common_app/widgets/common_widgets/time_slots.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; -class AppointmentServicesListView extends StatelessWidget { - AppointmentServicesListView({Key? key}) : super(key: key); +class BookAppointmentServicesView extends StatelessWidget { + BookAppointmentServicesView({Key? key}) : super(key: key); void openTheAddServiceBottomSheet(BuildContext context, AppointmentsVM appointmentsVM) { showModalBottomSheet( 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 6ea7081..63a2cb9 100644 --- a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart +++ b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart @@ -41,18 +41,12 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { children: [ Builder( builder: (context) { - List serviceCategories = []; - for (var element in appointmentsVM.branchCategories) { - if (!element.isSelected!) { - serviceCategories.add(DropValue(element.id?.toInt() ?? 0, element.categoryName ?? "", "")); - } - } return DropdownField( - (DropValue value) => appointmentsVM.updateProviderCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), - list: serviceCategories, + (DropValue value) => appointmentsVM.updateProviderCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), + list: appointmentsVM.branchCategories, hint: "Select Category", - dropdownValue: appointmentsVM.providerCategoryId.selectedId != -1 - ? DropValue(appointmentsVM.providerCategoryId.selectedId, appointmentsVM.providerCategoryId.selectedOption, "") + dropdownValue: appointmentsVM.branchSelectedCategoryId.selectedId != -1 + ? DropValue(appointmentsVM.branchSelectedCategoryId.selectedId, appointmentsVM.branchSelectedCategoryId.selectedOption, "") : null, ); }, @@ -68,17 +62,14 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { builder: (context) { List serviceCategories = []; for (var element in appointmentsVM.branchServices) { - if (!element.isSelected!) { - serviceCategories.add(DropValue(element.providerBranchServiceID ?? 0, element.providerBranchServiceID.toString(), "")); - } + serviceCategories.add(DropValue(element.serviceProviderServiceId ?? 0, element.serviceProviderServiceId.toString(), "")); } return DropdownField( - (DropValue value) => appointmentsVM.updateProviderServiceId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), + (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, + dropdownValue: + appointmentsVM.branchServiceId.selectedId != -1 ? DropValue(appointmentsVM.branchServiceId.selectedId, appointmentsVM.branchServiceId.selectedOption, "") : null, ); }, ), diff --git a/lib/views/dashboard/fragments/providers_fragment.dart b/lib/views/dashboard/fragments/providers_fragment.dart index 9419afd..f2f7593 100644 --- a/lib/views/dashboard/fragments/providers_fragment.dart +++ b/lib/views/dashboard/fragments/providers_fragment.dart @@ -1,60 +1,60 @@ -import 'package:car_customer_app/view_models/appointments_view_model.dart'; -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/theme/colors.dart'; -import 'package:mc_common_app/utils/navigator.dart'; -import 'package:mc_common_app/widgets/common_widgets/categories_list.dart'; -import 'package:mc_common_app/widgets/common_widgets/provider_details_card.dart'; -import 'package:provider/provider.dart'; - -class ProvidersFragment extends StatelessWidget { - const ProvidersFragment({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - color: MyColors.backgroundColor, - width: double.infinity, - height: double.infinity, - child: Consumer( - builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) { - return Column( - children: [ - 16.height, - FiltersList( - filterList: appointmentsVM.providersFilterOptions, - onFilterTapped: (index, selectedFilterId) => appointmentsVM.applyFilterOnProviders(index: index), - ), - 16.height, - Expanded( - child: Container( - child: ListView.builder( - shrinkWrap: true, - itemCount: 30, - itemBuilder: (BuildContext context, int index) { - return ProviderDetailsCard( - onCardTapped: () { - if (context.read().providerCategories.isEmpty) { - context.read().getProviderCategories(); - } - navigateWithName(context, AppRoutes.bookAppointmenServicesView); - }, - providerImageUrl: MyAssets.bnCar, - title: "", - providerLocation: " 3km", - providerName: "Al Ahmed Maintenance", - providerRatings: "4.9", - ); - }, - ), - ), - ), - ], - ); - }, - ), - ); - } -} +// import 'package:car_customer_app/view_models/appointments_view_model.dart'; +// 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/theme/colors.dart'; +// import 'package:mc_common_app/utils/navigator.dart'; +// import 'package:mc_common_app/widgets/common_widgets/categories_list.dart'; +// import 'package:mc_common_app/widgets/common_widgets/provider_details_card.dart'; +// import 'package:provider/provider.dart'; +// +// class ProvidersFragment extends StatelessWidget { +// const ProvidersFragment({Key? key}) : super(key: key); +// +// @override +// Widget build(BuildContext context) { +// return Container( +// color: MyColors.backgroundColor, +// width: double.infinity, +// height: double.infinity, +// child: Consumer( +// builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) { +// return Column( +// children: [ +// 16.height, +// FiltersList( +// filterList: appointmentsVM.providersFilterOptions, +// onFilterTapped: (index, selectedFilterId) => appointmentsVM.applyFilterOnProviders(index: index), +// ), +// 16.height, +// Expanded( +// child: Container( +// child: ListView.builder( +// shrinkWrap: true, +// itemCount: 30, +// itemBuilder: (BuildContext context, int index) { +// return ProviderDetailsCard( +// onCardTapped: () { +// if (context.read().branchCategories.isEmpty) { +// context.read().getBranchCategories(); +// } +// navigateWithName(context, AppRoutes.bookAppointmenServicesView); +// }, +// providerImageUrl: MyAssets.bnCar, +// title: "", +// providerLocation: " 3km", +// providerName: "Al Ahmed Maintenance", +// providerRatings: "4.9", +// ); +// }, +// ), +// ), +// ), +// ], +// ); +// }, +// ), +// ); +// } +// } diff --git a/lib/views/provider/sheet/items_list_sheet.dart b/lib/views/provider/sheet/items_list_sheet.dart index 7870b53..25cb5f4 100644 --- a/lib/views/provider/sheet/items_list_sheet.dart +++ b/lib/views/provider/sheet/items_list_sheet.dart @@ -2,6 +2,7 @@ import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:flutter/material.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/widgets/empty_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; @@ -28,66 +29,65 @@ class _ItemsListSheetState extends State { return SizedBox( height: MediaQuery.of(context).size.height / 1.2, child: Consumer( - builder: (context, model, _) { - return model.serviceItems == null - ? const Center(child: CircularProgressIndicator()) - : model.serviceItems!.data!.isEmpty - ? const EmptyWidget() - : ListView.separated( - itemBuilder: (BuildContext context, int index) { - return SizedBox( - width: double.infinity, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + builder: (context, appointmentsVM, _) { + return appointmentsVM.serviceItems.isEmpty + ? const EmptyWidget() + : ListView.separated( + itemCount: appointmentsVM.serviceItems.length, + itemBuilder: (BuildContext context, int index) { + ServiceItemModel serviceItemModel = appointmentsVM.serviceItems[index]; + return SizedBox( + width: double.infinity, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + serviceItemModel.name.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), + showItem("Allowing home service:", (serviceItemModel.isAppointmentCustomerLoc ?? false) ? "Yes" : "No", valueColor: Colors.green), + 12.height, + "Service Amount".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + Row( + crossAxisAlignment: CrossAxisAlignment.end, children: [ - model.serviceItems!.data![index].name.toString().toText(fontSize: 16, isBold: true), - 4.height, - showItem("Available for appointment:", (model.serviceItems!.data![index].isAllowAppointment ?? false) ? "Yes" : "No", valueColor: Colors.green), - showItem("Allowing Workshop service:", (model.serviceItems!.data![index].isAppointmentCompanyLoc ?? false) ? "Yes" : "No", valueColor: Colors.green), - showItem("Allowing home service:", (model.serviceItems!.data![index].isAppointmentCustomerLoc ?? false) ? "Yes" : "No", valueColor: Colors.green), - 12.height, - "Service Amount".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - model.serviceItems!.data![index].price!.toText(fontSize: 22, isBold: true), - 2.width, - Padding( - padding: const EdgeInsets.only(bottom: 4), - child: "SAR".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - ), - ], + serviceItemModel.price!.toText(fontSize: 22, isBold: true), + 2.width, + Padding( + padding: const EdgeInsets.only(bottom: 4), + child: "SAR".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), ), ], ), - ), - // Padding( - // padding: const EdgeInsets.all(4.0), - // child: SvgPicture.asset( - // MyAssets.icEdit, - // width: 16, - // height: 16, - // ), - // ) - ], + ], + ), ), - ).toWhiteContainer( - width: double.infinity, - allPading: 12, - ); - }, - separatorBuilder: (BuildContext context, int index) { - return 12.height; - }, - padding: const EdgeInsets.all(20), - itemCount: model.serviceItems!.data!.length, + // Padding( + // padding: const EdgeInsets.all(4.0), + // child: SvgPicture.asset( + // MyAssets.icEdit, + // width: 16, + // height: 16, + // ), + // ) + ], + ), + ).toWhiteContainer( + width: double.infinity, + allPading: 12, ); + }, + separatorBuilder: (BuildContext context, int index) { + return 12.height; + }, + padding: const EdgeInsets.all(20), + ); }, ), ); From 533952e14e4c9438abaa662c292001d509faddb2 Mon Sep 17 00:00:00 2001 From: FaizHashmiCS22 Date: Tue, 18 Jul 2023 16:47:00 +0300 Subject: [PATCH 08/12] 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), From e6c6c127f08d6199a1ff3feab15943ade4f48290 Mon Sep 17 00:00:00 2001 From: FaizHashmiCS22 Date: Wed, 26 Jul 2023 07:51:28 +0300 Subject: [PATCH 09/12] Schedules in appointments (In Progress) --- lib/repositories/schedule_repo.dart | 18 ++ lib/view_models/appointments_view_model.dart | 75 +++++-- .../book_appointment_services_view.dart | 80 +++++--- .../book_appointments_item_view.dart | 37 ++-- .../appointments/review_appointment_view.dart | 186 ++++++++++++------ 5 files changed, 273 insertions(+), 123 deletions(-) diff --git a/lib/repositories/schedule_repo.dart b/lib/repositories/schedule_repo.dart index 7edfe49..7938095 100644 --- a/lib/repositories/schedule_repo.dart +++ b/lib/repositories/schedule_repo.dart @@ -6,6 +6,7 @@ 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'; +import 'package:mc_common_app/models/service_schedule_model.dart'; abstract class ScheduleRepo { Future getAllServices(String branchId); @@ -19,6 +20,8 @@ abstract class ScheduleRepo { Future> getSchedules(String branchId); Future updateServicesInSchedule(Map map); + + Future> mergeServiceIntoAvailableSchedules({required List serviceItemIds}); } class ScheduleRepoImp implements ScheduleRepo { @@ -67,4 +70,19 @@ class ScheduleRepoImp implements ScheduleRepo { String t = AppState().getUser.data!.accessToken ?? ""; return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateGroup, map, token: t); } + + Future> mergeServiceIntoAvailableSchedules({required List serviceItemIds}) async { + String t = AppState().getUser.data!.accessToken ?? ""; + var dummyServiceIds = ["3", "2"]; + var queryParameters = {"ServiceItemIDs": dummyServiceIds}; + GenericRespModel adsGenericModel = await injector.get().getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.GetServiceItemAppointmentScheduleSlots, + token: t, + queryParameters: queryParameters, + ); + List serviceAppointmentScheduleModel = + List.generate(adsGenericModel.data.length, (index) => ServiceAppointmentScheduleModel.fromJson(adsGenericModel.data[index])); + return serviceAppointmentScheduleModel; + } } diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index f3beb92..08889a9 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -1,12 +1,10 @@ -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/service_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'; @@ -29,7 +27,7 @@ class AppointmentsVM extends BaseVM { List myAppointments = []; List appointmentsFilterOptions = []; - List availableSchedules = []; + // List availableSchedules = []; bool isFetchingServices = false; @@ -37,12 +35,44 @@ class AppointmentsVM extends BaseVM { bool isHomeTapped = false; - Future getSchedulesByBranchId() async { - availableSchedules = await scheduleRepo.getSchedules(selectedBranchModel!.id!.toString()); - log("schedules: ${availableSchedules.toString()}"); + List serviceAppointmentScheduleList = []; + + // Future getSchedulesByBranchId() async { + // availableSchedules = await scheduleRepo.getSchedules(selectedBranchModel!.id!.toString()); + // log("schedules: ${availableSchedules.toString()}"); + // notifyListeners(); + // } + + List servicesInSchedule = []; + + Future mergeServiceIntoAvailableSchedules() async { + List serviceItemIds = []; + serviceItems.forEach((serviceItem) { + if (serviceItem.isUpdateOrSelected!) { + serviceItemIds.add(serviceItem.id!.toString()); + } + }); + var scheduleList = await scheduleRepo.mergeServiceIntoAvailableSchedules(serviceItemIds: serviceItemIds); + serviceAppointmentScheduleList.addAll(scheduleList); + servicesInSchedule.clear(); + serviceAppointmentScheduleList.forEach((schedule) { + schedule.serviceItemList!.forEach((item) { + if (!ifItemAlreadyThere(item.serviceProviderServiceId!)) { + servicesInSchedule.add(DropValue(item.serviceProviderServiceId!, item.description!, "")); + } + }); + }); notifyListeners(); } + bool ifItemAlreadyThere(int id) { + int index = servicesInSchedule.indexWhere((element) => element.id == id); + if (index == -1) { + return false; + } + return true; + } + void updateIsHomeTapped(bool value) { isHomeTapped = value; notifyListeners(); @@ -228,7 +258,6 @@ class AppointmentsVM extends BaseVM { List getFilteredBranchServices({required int categoryId}) { List filteredServices = selectedBranchModel!.branchServices!.where((element) => element.categoryId == categoryId).toList(); - log("returning: ${filteredServices.toString()}"); return filteredServices; } @@ -262,12 +291,26 @@ class AppointmentsVM extends BaseVM { return false; } - void mergeServiceInAvailableSchedule() { - for (var schedule in availableSchedules) { - for (var service in schedule.scheduleServices!) { - if (branchSelectedServiceId.selectedId == service.serviceId) {} - schedule.selectedServices!.add(currentServiceSelection!); - } - } - } +// void mergeServiceInAvailableSchedule() { +// log("schedules: ${availableSchedules}"); +// for (var schedule in availableSchedules) { +// for (var service in schedule.scheduleServices!) { +// if (branchSelectedServiceId.selectedId == service.serviceId) { +// log("ID matched: ${service.serviceId}"); +// log("SelectedServices: ${schedule.selectedServices}"); +// +// +// int isAlreadyThereIndex = schedule.selectedServices!.indexWhere((element) => element.serviceProviderServiceId == branchSelectedServiceId.selectedId); +// if (isAlreadyThereIndex != -1) { +// log("removing: ${service.serviceId}"); +// schedule.selectedServices!.removeAt(isAlreadyThereIndex); +// } +// schedule.selectedServices!.add(currentServiceSelection!); +// notifyListeners(); +// return; +// } +// } +// } +// notifyListeners(); +// } } diff --git a/lib/views/appointments/book_appointment_services_view.dart b/lib/views/appointments/book_appointment_services_view.dart index 186c40f..6fe778d 100644 --- a/lib/views/appointments/book_appointment_services_view.dart +++ b/lib/views/appointments/book_appointment_services_view.dart @@ -5,9 +5,8 @@ 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/service_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'; @@ -16,6 +15,7 @@ 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/common_widgets/info_bottom_sheet.dart'; import 'package:mc_common_app/widgets/common_widgets/time_slots.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'; @@ -122,6 +122,14 @@ class BookAppointmentServicesView extends StatelessWidget { }); } + String getTotalPrice(List serviceItems) { + var totalPrice = 0.0; + serviceItems.forEach((element) { + totalPrice = totalPrice + double.parse(element.price ?? "0.0"); + }); + return totalPrice.toString(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -151,24 +159,44 @@ class BookAppointmentServicesView extends StatelessWidget { 10.height, ListView.builder( shrinkWrap: true, - itemCount: appointmentsVM.availableSchedules.where((element) => element.selectedServices!.isNotEmpty).toList().length, + itemCount: appointmentsVM.serviceAppointmentScheduleList.length, itemBuilder: (BuildContext context, int scheduleIndex) { - ScheduleData scheduleData = appointmentsVM.availableSchedules.where((element) => element.selectedServices!.isNotEmpty).toList()[scheduleIndex]; + ServiceAppointmentScheduleModel scheduleData = appointmentsVM.serviceAppointmentScheduleList[scheduleIndex]; return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: "Schedule: ${scheduleData.scheduleName}".toText(fontSize: 20, isBold: true), + child: "Schedule: ${scheduleIndex + 1}".toText(fontSize: 20, isBold: true), ), Align( alignment: Alignment.topRight, - child: MyAssets.icEdit.buildSvg(fit: BoxFit.fill, height: 19, width: 19), + child: Icon( + Icons.delete_outline, + size: 28, + ), ).onPress(() {}), ], ), if (true) ...[ + 5.height, + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ("Available Dates").toText(fontSize: 14, isBold: true), + ], + ), + 5.height, + SizedBox( + width: double.infinity, + child: BuildTimeSlots( + timeSlots: scheduleData.availableDates!, + onPressed: (index) => null, + ), + ), + ], + if (scheduleData.selectedDate!.isNotEmpty) ...[ 5.height, Row( mainAxisAlignment: MainAxisAlignment.start, @@ -192,15 +220,15 @@ class BookAppointmentServicesView extends StatelessWidget { ListView.separated( physics: NeverScrollableScrollPhysics(), shrinkWrap: true, - itemCount: scheduleData.selectedServices!.length, + itemCount: appointmentsVM.servicesInSchedule.length, itemBuilder: (BuildContext context, int serviceIndex) { - ServiceModel selectedService = scheduleData.selectedServices![serviceIndex]; + DropValue selectedService = appointmentsVM.servicesInSchedule[serviceIndex]; return Column( children: [ Row( children: [ Expanded( - child: "${selectedService.serviceDescription}".toText(fontSize: 15, isBold: true), + child: selectedService.value.toText(fontSize: 15, isBold: true), ), ], ), @@ -212,10 +240,10 @@ class BookAppointmentServicesView extends StatelessWidget { ("Home").toText(fontSize: 12, isBold: true).expand(), ], ), - 8.height, + 5.height, Column( - children: List.generate(scheduleData.selectedServices![serviceIndex].serviceItems!.length, (itemIndex) { - ItemData itemData = selectedService.serviceItems![itemIndex]; + children: List.generate(scheduleData.serviceItemList!.length, (itemIndex) { + ItemData itemData = scheduleData.serviceItemList![itemIndex]; return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -225,25 +253,25 @@ class BookAppointmentServicesView extends StatelessWidget { ); }), ), - 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)), ], ], ); }, separatorBuilder: (BuildContext context, int index) => Divider(thickness: 2), - ) + ), + 8.height, + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + getTotalPrice(appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].serviceItemList ?? []).toText(fontSize: 32, 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)), ], ), ], diff --git a/lib/views/appointments/book_appointments_item_view.dart b/lib/views/appointments/book_appointments_item_view.dart index d375465..2620166 100644 --- a/lib/views/appointments/book_appointments_item_view.dart +++ b/lib/views/appointments/book_appointments_item_view.dart @@ -2,6 +2,7 @@ import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:car_customer_app/views/appointments/widgets/service_item_with_price_checkbox.dart'; 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/services/item_model.dart'; @@ -68,7 +69,7 @@ class BookAppointmentsItemView extends StatelessWidget { title: itemData.name!, isSelected: itemData.isUpdateOrSelected!, onSelection: (bool value) { - appointmentsVM.onItemUpdateOrSelected(index, !itemData.isUpdateOrSelected!, itemData!.id!); + appointmentsVM.onItemUpdateOrSelected(index, !itemData.isUpdateOrSelected!, itemData.id!); }, priceWidget: Row( crossAxisAlignment: CrossAxisAlignment.end, @@ -112,25 +113,25 @@ class BookAppointmentsItemView extends StatelessWidget { ), 12.width, Expanded( - child: ShowFillButton( - maxHeight: 55, - title: "Next", - 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(); + child: Builder( + builder: (BuildContext context) { + return ShowFillButton( + maxHeight: 55, + title: "Next", + onPressed: () async { + bool resp = appointmentsVM.onSubServicesNextPressed(); + if (resp) { + Utils.showLoading(context); + await appointmentsVM.mergeServiceIntoAvailableSchedules(); + appointmentsVM.resetCategorySelectionBottomSheet(); + Navigator.of(context).pushNamedAndRemoveUntil(AppRoutes.bookAppointmenServicesView, (Route route) => false); - } + // appointmentsVM.mergeServiceInAvailableSchedule(); + } + }, + backgroundColor: !appointmentsVM.isServiceSelectionValidated() ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.primaryColor, + ); }, - backgroundColor: !appointmentsVM.isServiceSelectionValidated() ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.primaryColor, ), ), ], diff --git a/lib/views/appointments/review_appointment_view.dart b/lib/views/appointments/review_appointment_view.dart index 96c8a88..841f4c0 100644 --- a/lib/views/appointments/review_appointment_view.dart +++ b/lib/views/appointments/review_appointment_view.dart @@ -1,23 +1,24 @@ +import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:easy_localization/easy_localization.dart'; 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/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/service_schedule_model.dart'; +import 'package:mc_common_app/models/services/item_model.dart'; import 'package:mc_common_app/theme/colors.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/dropdown/dropdow_field.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:provider/provider.dart'; class ReviewAppointment extends StatelessWidget { const ReviewAppointment({Key? key}) : super(key: key); - Widget buildBranchInfoCard({ - required String branchImageUrl, - required String branchName, - required String branchRatings, - required String branchLocation, - }) { + Widget buildBranchInfoCard({required BuildContext context}) { + AppointmentsVM appointmentsVM = context.read(); return Padding( padding: const EdgeInsets.only( bottom: 10, @@ -27,7 +28,7 @@ class ReviewAppointment extends StatelessWidget { child: Row( children: [ Image.asset( - branchImageUrl, + MyAssets.bnCar, width: 80, height: 60, fit: BoxFit.cover, @@ -46,12 +47,12 @@ class ReviewAppointment extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - branchName.toText(fontSize: 16, isBold: true), + (appointmentsVM.selectedBranchModel!.branchName ?? "").toText(fontSize: 16, isBold: true), Row( children: [ LocaleKeys.location.tr().toText(color: MyColors.lightTextColor, fontSize: 12), 2.width, - ":$branchLocation".toText(fontSize: 12), + ": ${appointmentsVM.selectedBranchModel!.branchDescription ?? ""}".toText(fontSize: 12), ], ), ], @@ -60,7 +61,7 @@ class ReviewAppointment extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - branchRatings.toText( + "4.9".toText( isUnderLine: true, isBold: true, fontSize: 12, @@ -78,44 +79,100 @@ class ReviewAppointment extends StatelessWidget { ).onPress(() {}).toWhiteContainer(width: double.infinity, allPading: 12)); } - Widget buildServicesSubSection({required String title, required List itemList}) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - title.toText(fontSize: 16, isBold: true), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: List.generate( - itemList.length, - (index) => itemList[index].toText( - fontSize: 12, - color: MyColors.lightTextColor, - isBold: true, - )), - ), - ], - ).paddingOnly(bottom: 14); - } - - Widget buildServicesInfoCard() { - return SizedBox( - width: double.infinity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: List.generate( - 2, - (index) => buildServicesSubSection( - title: "Mechanic", - itemList: [ - "Engine Check : 450 SAR", - "Gear Check : 700 SAR", - "Gear Check : 700 SAR", - "Gear Check : 700 SAR", - ], - ), - ), - ).toWhiteContainer(width: double.infinity, allPading: 12, margin: EdgeInsets.symmetric(horizontal: 21)), + Widget buildServicesInfoCard({required BuildContext context}) { + AppointmentsVM appointmentsVM = context.read(); + return ListView.builder( + shrinkWrap: true, + itemCount: appointmentsVM.serviceAppointmentScheduleList.length, + itemBuilder: (BuildContext context, int scheduleIndex) { + ServiceAppointmentScheduleModel scheduleData = appointmentsVM.serviceAppointmentScheduleList[scheduleIndex]; + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: "Schedule: ${scheduleIndex + 1}".toText(fontSize: 20, isBold: true), + ), + ], + ), + 10.height, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.separated( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: appointmentsVM.servicesInSchedule.length, + itemBuilder: (BuildContext context, int serviceIndex) { + DropValue selectedService = appointmentsVM.servicesInSchedule[serviceIndex]; + return Column( + children: [ + Row( + children: [ + Expanded( + child: selectedService.value.toText(fontSize: 15, isBold: true), + ), + ], + ), + if (true) ...[ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Service Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + ("Home").toText(fontSize: 12, isBold: true).expand(), + ], + ), + 5.height, + Column( + children: List.generate(scheduleData.serviceItemList!.length, (itemIndex) { + ItemData itemData = scheduleData.serviceItemList![itemIndex]; + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "${itemData.name}: ".toText(fontSize: 13, color: MyColors.lightTextColor, isBold: true), + ], + ); + }), + ), + ], + ], + ); + }, + separatorBuilder: (BuildContext context, int index) => Divider(thickness: 2), + ).paddingOnly(bottom: 10), + ], + ), + ], + ).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 10)); + }, ); + // AppointmentsVM appointmentsVM = context.read(); + // return SizedBox( + // width: double.infinity, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: List.generate( + // appointmentsVM.servicesInSchedule.length, + // (serviceIndex) => Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // appointmentsVM.servicesInSchedule[serviceIndex].value.toText(fontSize: 16, isBold: true), + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: List.generate( + // appointmentsVM, + // (index) => itemList[index].toText( + // fontSize: 12, + // color: MyColors.lightTextColor, + // isBold: true, + // )), + // ), + // ], + // ).paddingOnly(bottom: 14), + // ), + // ).toWhiteContainer(width: double.infinity, allPading: 12, margin: EdgeInsets.symmetric(horizontal: 21)), + // ); } Widget buildTimeAndLocationInfoCard() { @@ -139,19 +196,29 @@ class ReviewAppointment extends StatelessWidget { ); } - Widget buildChargesBreakDown() { + Widget buildChargesBreakDown({required BuildContext context}) { + AppointmentsVM appointmentsVM = context.read(); + List allSelectedItems = []; + double totalServicePrice = 0.0; + appointmentsVM.serviceAppointmentScheduleList.forEach((schedule) { + schedule.serviceItemList!.forEach((item) { + allSelectedItems.add(item); + totalServicePrice = totalServicePrice + double.parse(item.price!); + }); + }); + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "Services".toText(fontSize: 16, isBold: true), Column( children: List.generate( - 5, + allSelectedItems.length, (index) => Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - "Gear Kit".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - "200 SAR".toText(fontSize: 12, isBold: true), + "${allSelectedItems[index].name}".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + "${allSelectedItems[index].price} SAR".toText(fontSize: 12, isBold: true), ], ), ), @@ -159,13 +226,11 @@ class ReviewAppointment extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - "1149 SAR".toText(fontSize: 16, isBold: true), + "${totalServicePrice.toString()} SAR".toText(fontSize: 16, isBold: true), ], ), 10.height, - Divider( - thickness: 0.7, - ), + Divider(thickness: 0.7), "Home Location".toText(fontSize: 16, isBold: true), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -245,15 +310,10 @@ class ReviewAppointment extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - buildBranchInfoCard( - branchImageUrl: MyAssets.bnCar, - branchLocation: " 3km", - branchName: "Al Ahmed Maintenance", - branchRatings: "4.9", - ), - buildServicesInfoCard(), + buildBranchInfoCard(context: context), + buildServicesInfoCard(context: context), buildTimeAndLocationInfoCard(), - buildChargesBreakDown(), + buildChargesBreakDown(context: context), ], ), ), From ce9aab8db7fd59ee9e3423b0fa830cb93b2d6402 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Tue, 5 Sep 2023 12:09:33 +0300 Subject: [PATCH 10/12] ad vehicle type --- android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- ios/Flutter/AppFrameworkInfo.plist | 2 +- ios/Runner.xcodeproj/project.pbxproj | 56 +++++++++++++++---- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- ios/Runner/Info.plist | 12 +++- lib/config/customer_routes.dart | 2 + .../dashboard/fragments/ads_fragment.dart | 2 +- lib/views/provider/branch_detail_page.dart | 5 +- .../provider/sheet/items_list_sheet.dart | 5 +- pubspec.yaml | 2 +- 11 files changed, 65 insertions(+), 27 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 543cb25..aca8b4a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e547954..53d5a7c 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 8d4492f..9625e10 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 899dfbb..d193b7b 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -68,7 +68,6 @@ C74C73D850C4D4FC94940060 /* Pods-Runner.release.xcconfig */, 3643750A79F4A25346F39B6F /* Pods-Runner.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -140,6 +139,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 9A2045E27F192C87BCD2FA50 /* [CP] Embed Pods Frameworks */, + 4CC2E88FCFA89EAD7EEA637B /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -156,7 +156,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -200,10 +200,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -212,8 +214,26 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 4CC2E88FCFA89EAD7EEA637B /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -340,7 +360,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -356,9 +376,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 99Z3UD3LJM; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.carCustomerApp; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -414,7 +439,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -463,11 +488,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -480,9 +506,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 99Z3UD3LJM; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.carCustomerApp; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -499,9 +530,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 99Z3UD3LJM; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.carCustomerApp; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140c..3db53b6 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + NSCameraUsageDescription + Used to access media images for car parts CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -28,18 +30,22 @@ Main UISupportedInterfaceOrientations - UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/lib/config/customer_routes.dart b/lib/config/customer_routes.dart index 6c1851e..63046f2 100644 --- a/lib/config/customer_routes.dart +++ b/lib/config/customer_routes.dart @@ -13,6 +13,7 @@ import 'package:mc_common_app/models/appointments_models/appointment_list_model. import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; import 'package:mc_common_app/views/advertisement/ads_detail_view.dart'; import 'package:mc_common_app/views/advertisement/create_ad_view.dart'; +import 'package:mc_common_app/views/advertisement/select_ad_type_view.dart'; import 'package:mc_common_app/views/payments/payment_methods_view.dart'; class CustomerAppRoutes { @@ -22,6 +23,7 @@ class CustomerAppRoutes { AppRoutes.appointmentDetailView: (context) => AppointmentDetailView(appointmentListModel: ModalRoute.of(context)!.settings.arguments as AppointmentListModel), AppRoutes.adsDetailView: (context) => AdsDetailView(adDetails: ModalRoute.of(context)!.settings.arguments as AdDetailsModel), AppRoutes.createAdView: (context) => CreateAdView(), + AppRoutes.selectAdTypeView: (context) => SelectAdTypeView(), AppRoutes.bookAppointmenServicesView: (context) => BookAppointmentServicesView(), AppRoutes.bookAppointmentsItemView: (context) => BookAppointmentsItemView(), AppRoutes.reviewAppointmentView: (context) => ReviewAppointment(), diff --git a/lib/views/dashboard/fragments/ads_fragment.dart b/lib/views/dashboard/fragments/ads_fragment.dart index 3201ffa..dc7c1e3 100644 --- a/lib/views/dashboard/fragments/ads_fragment.dart +++ b/lib/views/dashboard/fragments/ads_fragment.dart @@ -107,7 +107,7 @@ class AdsFragment extends StatelessWidget { context.read().getVehicleTypes(); } - navigateWithName(context, AppRoutes.createAdView); + navigateWithName(context, AppRoutes.selectAdTypeView); }, backgroundColor: MyColors.darkPrimaryColor, child: Icon( diff --git a/lib/views/provider/branch_detail_page.dart b/lib/views/provider/branch_detail_page.dart index 4849fe8..34092a3 100644 --- a/lib/views/provider/branch_detail_page.dart +++ b/lib/views/provider/branch_detail_page.dart @@ -155,10 +155,7 @@ class _BranchDetailPageState extends State { navigateWithName(context, AppRoutes.bookAppointmenServicesView); context.read().updateSelectedBranch(widget.branchDetailModel); }, - ).toContainer( - paddingAll: 0, - backgroundColor: Colors.white, - ), + ).toContainer(paddingAll: 0, backgroundColor: Colors.white), ], ), ); diff --git a/lib/views/provider/sheet/items_list_sheet.dart b/lib/views/provider/sheet/items_list_sheet.dart index 4d09e11..4738e2d 100644 --- a/lib/views/provider/sheet/items_list_sheet.dart +++ b/lib/views/provider/sheet/items_list_sheet.dart @@ -78,10 +78,7 @@ class _ItemsListSheetState extends State { // ) ], ), - ).toWhiteContainer( - width: double.infinity, - allPading: 12, - ); + ).toWhiteContainer(width: double.infinity, allPading: 12); }, separatorBuilder: (BuildContext context, int index) { return 12.height; diff --git a/pubspec.yaml b/pubspec.yaml index c845519..a1a1545 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: cupertino_icons: ^1.0.2 mc_common_app: - path: D:/Development/car_common_app/ + path: /Volumes/Data/Projects/Flutter/car_common_app dev_dependencies: flutter_test: From 045ccc6f7044ff223524b816ca81153464749714 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Tue, 12 Sep 2023 16:45:12 +0300 Subject: [PATCH 11/12] Ad Details Page Checls --- lib/config/customer_routes.dart | 4 +- .../book_appointment_services_view.dart | 4 +- lib/views/dashboard/dashboard_page.dart | 13 ++++- .../dashboard/fragments/ads_fragment.dart | 48 ++++++++++++++----- pubspec.yaml | 2 +- 5 files changed, 54 insertions(+), 17 deletions(-) diff --git a/lib/config/customer_routes.dart b/lib/config/customer_routes.dart index 63046f2..31326d4 100644 --- a/lib/config/customer_routes.dart +++ b/lib/config/customer_routes.dart @@ -15,6 +15,7 @@ import 'package:mc_common_app/views/advertisement/ads_detail_view.dart'; import 'package:mc_common_app/views/advertisement/create_ad_view.dart'; import 'package:mc_common_app/views/advertisement/select_ad_type_view.dart'; import 'package:mc_common_app/views/payments/payment_methods_view.dart'; +import 'package:mc_common_app/views/advertisement/ads_search_filter_view.dart'; class CustomerAppRoutes { static final Map routes = { @@ -23,7 +24,8 @@ class CustomerAppRoutes { AppRoutes.appointmentDetailView: (context) => AppointmentDetailView(appointmentListModel: ModalRoute.of(context)!.settings.arguments as AppointmentListModel), AppRoutes.adsDetailView: (context) => AdsDetailView(adDetails: ModalRoute.of(context)!.settings.arguments as AdDetailsModel), AppRoutes.createAdView: (context) => CreateAdView(), - AppRoutes.selectAdTypeView: (context) => SelectAdTypeView(), + AppRoutes.adsSearchFilterScreen: (context) => AdsSearchFilterView(), + AppRoutes.selectAdTypeView: (context) => SelectAdTypeView(isProvider: ModalRoute.of(context)!.settings.arguments as bool), AppRoutes.bookAppointmenServicesView: (context) => BookAppointmentServicesView(), AppRoutes.bookAppointmentsItemView: (context) => BookAppointmentsItemView(), AppRoutes.reviewAppointmentView: (context) => ReviewAppointment(), diff --git a/lib/views/appointments/book_appointment_services_view.dart b/lib/views/appointments/book_appointment_services_view.dart index 6fe778d..2212426 100644 --- a/lib/views/appointments/book_appointment_services_view.dart +++ b/lib/views/appointments/book_appointment_services_view.dart @@ -147,9 +147,11 @@ class BookAppointmentServicesView extends StatelessWidget { children: [ 21.height, CustomAddButton( + needsBorder: true, + bgColor: MyColors.white, onTap: () => openTheAddServiceBottomSheet(context, appointmentsVM), text: "Add Services", - icon: Container( + icon: Container( height: 24, width: 24, decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor), diff --git a/lib/views/dashboard/dashboard_page.dart b/lib/views/dashboard/dashboard_page.dart index 8b55571..086b261 100644 --- a/lib/views/dashboard/dashboard_page.dart +++ b/lib/views/dashboard/dashboard_page.dart @@ -10,6 +10,8 @@ import 'package:car_customer_app/views/dashboard/widgets/bottom_nav_bar.dart'; import 'package:car_customer_app/views/dashboard/widgets/drawer_widget.dart'; 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/utils/navigator.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; @@ -87,6 +89,7 @@ class _DashboardPageState extends State { @override Widget build(BuildContext context) { bool isHomePage = context.watch().selectedNavbarBarIndex == 2; + bool isAdsPage = context.watch().selectedNavbarBarIndex == 3; return Scaffold( appBar: CustomAppBar( backgroundColor: null, @@ -95,7 +98,15 @@ class _DashboardPageState extends State { profileImageUrl: MyAssets.bnCar, isRemoveBackButton: true, isDrawerEnabled: isHomePage ? true : false, - actions: [(isHomePage ? MyAssets.notificationsBellIcon : MyAssets.searchIcon).buildSvg().paddingOnly(right: 21)], + actions: [ + (isHomePage + ? MyAssets.notificationsBellIcon.buildSvg() + : InkWell( + onTap: () => navigateWithName(context, AppRoutes.adsSearchFilterScreen), + child: MyAssets.searchIcon.buildSvg(), + )) + .paddingOnly(right: 21) + ], ), drawer: CustomDrawer(dashboardVM: context.watch()), bottomNavigationBar: CustomBottomNavbar(), diff --git a/lib/views/dashboard/fragments/ads_fragment.dart b/lib/views/dashboard/fragments/ads_fragment.dart index dc7c1e3..faa4094 100644 --- a/lib/views/dashboard/fragments/ads_fragment.dart +++ b/lib/views/dashboard/fragments/ads_fragment.dart @@ -1,8 +1,12 @@ import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/config/dependencies.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/models/advertisment_models/ad_details_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/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/views/advertisement/ads_list.dart'; @@ -20,6 +24,20 @@ class AdsFragment extends StatelessWidget { await adVM.getMyAds(); } + List getAdsList(AdVM adVM) { + if (adVM.isExploreAdsTapped) { + if (adVM.exploreAdsFilteredList.isNotEmpty) { + return adVM.exploreAdsFilteredList; + } else { + return adVM.exploreAds; + } + } + if (adVM.myAdsFilteredList.isNotEmpty) { + return adVM.myAdsFilteredList; + } + return []; + } + @override Widget build(BuildContext context) { return Scaffold( @@ -85,15 +103,7 @@ class AdsFragment extends StatelessWidget { onRefresh: () => onRefreshAds(context), child: Consumer( builder: (BuildContext context, AdVM adVM, Widget? child) { - return BuildAdsList( - isAdsFragment: true, - shouldShowAdStatus: !adVM.isExploreAdsTapped, - adsList: adVM.isExploreAdsTapped && adVM.exploreAdsFilteredList.isNotEmpty - ? adVM.exploreAdsFilteredList - : !adVM.isExploreAdsTapped && adVM.myAdsFilteredList.isNotEmpty - ? adVM.myAdsFilteredList - : [], - ); + return BuildAdsList(isAdsFragment: true, shouldShowAdStatus: !adVM.isExploreAdsTapped, adsList: getAdsList(adVM)); }, ), ), @@ -102,12 +112,24 @@ class AdsFragment extends StatelessWidget { ), ), floatingActionButton: FloatingActionButton( - onPressed: () { - if (context.read().vehicleTypes.isEmpty) { - context.read().getVehicleTypes(); + onPressed: () async { + final adVM = context.read(); + if (adVM.vehicleTypes.isEmpty) { + await adVM.getVehicleTypes(); } - navigateWithName(context, AppRoutes.selectAdTypeView); + if (adVM.vehicleAdsDurations.isEmpty) { + await adVM.getVehicleAdsDuration(); + } + adVM.updateVehicleAdDurationId( + SelectionModel( + selectedId: adVM.vehicleAdsDurations.first.id ?? 0, + selectedOption: "${adVM.vehicleAdsDurations.first.days} Days", + itemPrice: adVM.vehicleAdsDurations.first.price!.toInt().toString(), + ), + ); + + navigateWithName(context, AppRoutes.selectAdTypeView, arguments: injector.get().currentAppType == AppType.provider); }, backgroundColor: MyColors.darkPrimaryColor, child: Icon( diff --git a/pubspec.yaml b/pubspec.yaml index a1a1545..3c4aa16 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -74,7 +74,7 @@ flutter: # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a + # "family" key with the font family name, and a "fonts" key with a # list giving the asset and other descriptors for the font. For # example: # fonts: From fa35392611e8ea13728ec6e6fb3e47e381a20bee Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Sun, 17 Sep 2023 15:04:24 +0300 Subject: [PATCH 12/12] Ad Module Page --- lib/config/customer_routes.dart | 5 ++- lib/views/dashboard/dashboard_page.dart | 17 ++++++++ .../dashboard/fragments/ads_fragment.dart | 40 ++++++------------- .../dashboard/fragments/home_fragment.dart | 3 +- 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/lib/config/customer_routes.dart b/lib/config/customer_routes.dart index 31326d4..6cca179 100644 --- a/lib/config/customer_routes.dart +++ b/lib/config/customer_routes.dart @@ -11,13 +11,14 @@ import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/models/advertisment_models/ad_details_model.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/utils/enums.dart'; import 'package:mc_common_app/views/advertisement/ads_detail_view.dart'; import 'package:mc_common_app/views/advertisement/create_ad_view.dart'; import 'package:mc_common_app/views/advertisement/select_ad_type_view.dart'; import 'package:mc_common_app/views/payments/payment_methods_view.dart'; import 'package:mc_common_app/views/advertisement/ads_search_filter_view.dart'; -class CustomerAppRoutes { + class CustomerAppRoutes { static final Map routes = { AppRoutes.dashboard: (context) => DashboardPage(), AppRoutes.bookProviderAppView: (context) => BookProviderAppView(), @@ -29,7 +30,7 @@ class CustomerAppRoutes { AppRoutes.bookAppointmenServicesView: (context) => BookAppointmentServicesView(), AppRoutes.bookAppointmentsItemView: (context) => BookAppointmentsItemView(), AppRoutes.reviewAppointmentView: (context) => ReviewAppointment(), - AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(), + AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(paymentType: ModalRoute.of(context)!.settings.arguments as PaymentTypesEnum), 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/views/dashboard/dashboard_page.dart b/lib/views/dashboard/dashboard_page.dart index 086b261..7145d88 100644 --- a/lib/views/dashboard/dashboard_page.dart +++ b/lib/views/dashboard/dashboard_page.dart @@ -11,6 +11,7 @@ import 'package:car_customer_app/views/dashboard/widgets/drawer_widget.dart'; 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/models/widgets_models.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; @@ -55,6 +56,22 @@ class _DashboardPageState extends State { if (adVm.exploreAds.isEmpty) { await adVm.getExploreAds(); } + + final adVM = context.read(); + if (adVM.vehicleTypes.isEmpty) { + await adVM.getVehicleTypes(); + } + + if (adVM.vehicleAdsDurations.isEmpty) { + await adVM.getVehicleAdsDuration(); + } + adVM.updateVehicleAdDurationId( + SelectionModel( + selectedId: adVM.vehicleAdsDurations.first.id ?? 0, + selectedOption: "${adVM.vehicleAdsDurations.first.days} Days", + itemPrice: adVM.vehicleAdsDurations.first.price!.toInt().toString(), + ), + ); } fetchUsername() async {} diff --git a/lib/views/dashboard/fragments/ads_fragment.dart b/lib/views/dashboard/fragments/ads_fragment.dart index faa4094..0bfc170 100644 --- a/lib/views/dashboard/fragments/ads_fragment.dart +++ b/lib/views/dashboard/fragments/ads_fragment.dart @@ -3,6 +3,7 @@ import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/config/dependencies.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/advertisment_models/ad_details_model.dart'; import 'package:mc_common_app/models/widgets_models.dart'; import 'package:mc_common_app/theme/colors.dart'; @@ -26,11 +27,7 @@ class AdsFragment extends StatelessWidget { List getAdsList(AdVM adVM) { if (adVM.isExploreAdsTapped) { - if (adVM.exploreAdsFilteredList.isNotEmpty) { - return adVM.exploreAdsFilteredList; - } else { - return adVM.exploreAds; - } + return adVM.exploreAdsFilteredList; } if (adVM.myAdsFilteredList.isNotEmpty) { return adVM.myAdsFilteredList; @@ -80,18 +77,21 @@ class AdsFragment extends StatelessWidget { ), ], ).horPaddingMain(), - if (adVM.isExploreAdsTapped && adVM.exploreAds.isNotEmpty) ...[ + if (adVM.isExploreAdsTapped) ...[ 16.height, - FiltersList(filterList: adVM.exploreAdsFilterOptions, onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnExploreAds(index: index), needLeftPadding: false) + FiltersList( + filterList: adVM.exploreAdsFilterOptions, + onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnExploreAds(index: index, createdByRoleFilter: selectedFilterId.toCreatedByRoleEnum()), + needLeftPadding: false) .paddingOnly(left: 21), ], - if (!adVM.isExploreAdsTapped && adVM.myAds.isNotEmpty) ...[ + if (!adVM.isExploreAdsTapped) ...[ 16.height, FiltersList( - filterList: adVM.myAdsFilterOptions, - onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnMyAds(index: index, selectedFilterId: selectedFilterId), - needLeftPadding: false) - .paddingOnly(left: 21), + filterList: adVM.myAdsFilterOptions, + onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnMyAds(index: index, adPostStatusEnum: selectedFilterId.toAdPostEnum()), + needLeftPadding: false, + ).paddingOnly(left: 21), ], ], ); @@ -113,22 +113,6 @@ class AdsFragment extends StatelessWidget { ), floatingActionButton: FloatingActionButton( onPressed: () async { - final adVM = context.read(); - if (adVM.vehicleTypes.isEmpty) { - await adVM.getVehicleTypes(); - } - - if (adVM.vehicleAdsDurations.isEmpty) { - await adVM.getVehicleAdsDuration(); - } - adVM.updateVehicleAdDurationId( - SelectionModel( - selectedId: adVM.vehicleAdsDurations.first.id ?? 0, - selectedOption: "${adVM.vehicleAdsDurations.first.days} Days", - itemPrice: adVM.vehicleAdsDurations.first.price!.toInt().toString(), - ), - ); - navigateWithName(context, AppRoutes.selectAdTypeView, arguments: injector.get().currentAppType == AppType.provider); }, backgroundColor: MyColors.darkPrimaryColor, diff --git a/lib/views/dashboard/fragments/home_fragment.dart b/lib/views/dashboard/fragments/home_fragment.dart index 9d10167..6a219cc 100644 --- a/lib/views/dashboard/fragments/home_fragment.dart +++ b/lib/views/dashboard/fragments/home_fragment.dart @@ -4,6 +4,7 @@ import 'package:car_customer_app/views/appointments/widgets/customer_appointment import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/views/advertisement/ads_list.dart'; import 'package:mc_common_app/widgets/common_widgets/my_service_provider.dart'; @@ -79,7 +80,7 @@ class HomeFragment extends StatelessWidget { onSubtitleTapped: () { context.read().onNavbarTapped(3); context.read().updateIsExploreAds(true); - context.read().applyFilterOnExploreAds(index: 0); + context.read().applyFilterOnExploreAds(index: 0, createdByRoleFilter: CreatedByRoleEnum.allAds); }, ).horPaddingMain(), BuildAdsList(