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/main.dart b/lib/main.dart index b3bd097..87a905d 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(); @@ -56,6 +58,7 @@ Future main() async { create: (_) => AppointmentsVM( providerRepo: injector.get(), commonServices: injector.get(), + branchRepo: injector.get(), commonRepo: injector.get(), ), ), 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..3d6355c --- /dev/null +++ b/lib/views/appointments/appointment_services_list_view.dart @@ -0,0 +1,254 @@ +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'; + +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"), + ]; + + 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( + 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), + Icon( + Icons.arrow_drop_down, + size: 30, + ) + ], + ).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, + ), + ), + ], + ], + ); + }), + ], + ).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 10)); + }, + ).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/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/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/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..6ea7081 --- /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.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, + 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.branchServices.isNotEmpty) ...[ + 8.height, + Builder( + builder: (context) { + List serviceCategories = []; + for (var element in appointmentsVM.branchServices) { + if (!element.isSelected!) { + 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.branchServiceId.selectedId != -1 + ? DropValue(appointmentsVM.branchServiceId.selectedId, appointmentsVM.branchServiceId.selectedOption, "") + : null, + ); + }, + ), + ], + if (appointmentsVM.branchServiceId.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, + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/views/dashboard/dashboard_page.dart b/lib/views/dashboard/dashboard_page.dart index abae722..8b55571 100644 --- a/lib/views/dashboard/dashboard_page.dart +++ b/lib/views/dashboard/dashboard_page.dart @@ -67,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/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); }, 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], ); },