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, + ], + ), + ), + ], + ), + ); + } +}