Added Appointments Design and Payments

merge-requests/2/head
FaizHashmiCS22 1 year ago
parent 521ff9ca38
commit 528adf68e8

@ -1,12 +1,14 @@
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/pick_items_view.dart';
import 'package:car_customer_app/views/dashboard/dashboard_page.dart';
import 'package:car_customer_app/views/providers/book_provider_app_view.dart';
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/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';
class CustomerAppRoutes {
static final Map<String, WidgetBuilder> routes = {
@ -15,5 +17,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.bookAppointmenServicesView: (context) => BookAppointmentServicesView(),
AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(),
};
}

@ -9,13 +9,17 @@ 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/models/post_params_model.dart';
import 'package:mc_common_app/repositories/ads_repo.dart';
import 'package:mc_common_app/repositories/common_repo.dart';
import 'package:mc_common_app/repositories/payments_repo.dart';
import 'package:mc_common_app/repositories/user_repo.dart';
import 'package:mc_common_app/services/services.dart';
import 'package:mc_common_app/services/common_services.dart';
import 'package:mc_common_app/services/payments_service.dart';
import 'package:mc_common_app/theme/app_theme.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/view_models/base_view_model.dart';
import 'package:mc_common_app/view_models/payment_view_model.dart';
import 'package:mc_common_app/view_models/user_view_model.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
@ -34,8 +38,8 @@ Future<void> main() async {
ChangeNotifierProvider<BaseVM>(create: (_) => BaseVM()),
ChangeNotifierProvider<DashboardVM>(
create: (_) => DashboardVM(
commonServices: injector.get<CommonAppServices>(),
userRepo: injector.get<UserRepo>(),
commonServices: injector.get<CommonServices>(),
),
),
ChangeNotifierProvider<UserVM>(
@ -43,22 +47,26 @@ Future<void> main() async {
),
ChangeNotifierProvider<AdVM>(
create: (_) => AdVM(
commonServices: injector.get<CommonServices>(),
commonServices: injector.get<CommonAppServices>(),
commonRepo: injector.get<CommonRepo>(),
adsRepo: injector.get<AdsRepo>(),
),
),
ChangeNotifierProvider<ProvidersVM>(
create: (_) => ProvidersVM(
commonServices: injector.get<CommonServices>(),
commonServices: injector.get<CommonAppServices>(),
commonRepo: injector.get<CommonRepo>(),
),
),
ChangeNotifierProvider<AppointmentsVM>(
create: (_) => AppointmentsVM(
commonServices: injector.get<CommonServices>(),
commonServices: injector.get<CommonAppServices>(),
commonRepo: injector.get<CommonRepo>(),
),
),
ChangeNotifierProvider<PaymentVM>(
create: (_) => PaymentVM(paymentService: injector.get<PaymentService>(), paymentRepo: injector.get<PaymentsRepo>()),
),
],
child: const MyApp(),
).setupLocale(),
@ -92,7 +100,7 @@ class MyApp extends StatelessWidget {
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
initialRoute: AppRoutes.splash,
initialRoute: AppRoutes.initialRoute,
routes: CustomerAppRoutes.routes,
);
},

@ -1,12 +1,14 @@
import 'package:flutter/cupertino.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';
import 'package:mc_common_app/models/widgets_models.dart';
import 'package:mc_common_app/repositories/common_repo.dart';
import 'package:mc_common_app/services/services.dart';
import 'package:mc_common_app/services/common_services.dart';
class AppointmentsVM extends ChangeNotifier {
final CommonRepo commonRepo;
final CommonServices commonServices;
final CommonAppServices commonServices;
AppointmentsVM({required this.commonServices, required this.commonRepo});
@ -15,6 +17,61 @@ class AppointmentsVM extends ChangeNotifier {
List<AppointmentListModel> myAppointments = [];
List<FilterListModel> appointmentsFilterOptions = [];
bool isFetchingServices = false;
List<ProviderCategoryModel> providerCategories = [];
bool isHomeTapped = false;
void updateIsHomeTapped(bool value) {
isHomeTapped = value;
notifyListeners();
}
String pickedHomeLocation = "";
void updatePickedHomeLocation(String value) {
pickedHomeLocation = value;
notifyListeners();
}
SelectionModel providerCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
void updateProviderCategoryId(SelectionModel id) async {
providerCategoryId = id;
await getProviderServices(id.selectedId);
notifyListeners();
}
List<ProviderServiceModel> providerServices = [];
SelectionModel providerServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
void updateProviderServiceId(SelectionModel id) async {
providerServiceId = id;
notifyListeners();
}
void getProviderCategories() async {
notifyListeners();
providerCategories = await commonRepo.getProviderServiceCategories();
notifyListeners();
}
getProviderServices(int categoryId) async {
providerServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
isHomeTapped = false;
pickedHomeLocation = "";
if (categoryId != -1) {
isFetchingServices = true;
notifyListeners();
providerServices = await commonRepo.getProviderServices(categoryId: categoryId);
isFetchingServices = false;
notifyListeners();
}
}
populateAppointmentsFilterList() {
appointmentsFilterOptions.clear();
appointmentsFilterOptions = [

@ -3,14 +3,14 @@ import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/user/image_response.dart';
import 'package:mc_common_app/repositories/user_repo.dart';
import 'package:mc_common_app/services/services.dart';
import 'package:mc_common_app/services/common_services.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/base_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:easy_localization/easy_localization.dart';
class DashboardVM extends BaseVM {
final CommonServices commonServices;
final CommonAppServices commonServices;
final UserRepo userRepo;
DashboardVM({required this.commonServices, required this.userRepo});

@ -1,11 +1,11 @@
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/services.dart';
import 'package:mc_common_app/services/common_services.dart';
class ProvidersVM extends ChangeNotifier {
final CommonRepo commonRepo;
final CommonServices commonServices;
final CommonAppServices commonServices;
ProvidersVM({required this.commonServices, required this.commonRepo});

@ -0,0 +1,325 @@
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<AppointmentsVM>();
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<DropValue> 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<DropValue> 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());
}
}

@ -90,19 +90,19 @@ class ServicesSelectionSection extends StatelessWidget {
"Select services you want".toText(fontSize: 18, isBold: true),
8.height,
DropdownField(
(DropValue value) {},
(DropValue value) {},
list: dropList,
hint: "Select service type",
),
8.height,
DropdownField(
(DropValue value) {},
(DropValue value) {},
list: dropList,
hint: "Select service type",
),
8.height,
DropdownField(
(DropValue value) {},
(DropValue value) {},
list: dropList,
hint: "Select service type",
),
@ -110,7 +110,7 @@ class ServicesSelectionSection extends StatelessWidget {
"Select date and time".toText(fontSize: 18, isBold: true),
8.height,
DropdownField(
(DropValue value) {},
(DropValue value) {},
list: dropList,
hint: "Select service type",
),

@ -82,19 +82,21 @@ class AdsFragment extends StatelessWidget {
16.height,
Expanded(
child: RefreshIndicator(
onRefresh: () => onRefreshAds(context),
child: Consumer(
builder: (BuildContext context, AdVM adVM, Widget? child) {
return BuildAdsList(
isAdsFragment: true,
adsList: adVM.isExploreAdsTapped && adVM.exploreAdsFilteredList.isNotEmpty
? adVM.exploreAdsFilteredList
: !adVM.isExploreAdsTapped && adVM.myAdsFilteredList.isNotEmpty
? adVM.myAdsFilteredList
: [],
);
},
)),
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
: [],
);
},
),
),
)
],
),

@ -48,9 +48,10 @@ class HomeFragment extends StatelessWidget {
const ServiceProviderWidget().horPaddingMain(),
Consumer(
builder: (BuildContext context, AdVM adVM, Widget? child) {
return adVM.myAds.isEmpty
? SizedBox()
: Column(
return Column(
children: [
if (adVM.myActiveAdsForHome.isNotEmpty) ...[
Column(
children: [
15.height,
ViewAllWidget(
@ -61,35 +62,41 @@ class HomeFragment extends StatelessWidget {
context.read<AdVM>().updateIsExploreAds(false);
}).horPaddingMain(),
BuildAdsList(
adsList: adVM.myAds.length >= 3 ? adVM.myAds.take(3).toList() : adVM.myAds,
shouldShowAdStatus: true,
isAdsFragment: false,
adsList: adVM.myActiveAdsForHome,
scrollPhysics: NeverScrollableScrollPhysics(),
),
],
);
},
),
20.height,
ViewAllWidget(
title: "My Recommended Ads".toUpperCase(),
subTitle: "View All",
onSubtitleTapped: () {
context.read<DashboardVM>().onNavbarTapped(3);
context.read<AdVM>().updateIsExploreAds(true);
context.read<AdVM>().applyFilterOnExploreAds(index: 0);
},
).horPaddingMain(),
Consumer(
builder: (BuildContext context, AdVM adVM, Widget? child) {
return BuildAdsList(
adsList: adVM.exploreAds.length >= 3 ? adVM.exploreAds.take(3).toList() : adVM.exploreAds,
// adsList: [],
isAdsFragment: true,
scrollPhysics: NeverScrollableScrollPhysics(),
)
],
if (adVM.exploreAds.isNotEmpty) ...[
Column(
children: [
15.height,
ViewAllWidget(
title: "My Recommended Ads".toUpperCase(),
subTitle: "View All",
onSubtitleTapped: () {
context.read<DashboardVM>().onNavbarTapped(3);
context.read<AdVM>().updateIsExploreAds(true);
context.read<AdVM>().applyFilterOnExploreAds(index: 0);
},
).horPaddingMain(),
BuildAdsList(
shouldShowAdStatus: false,
adsList: adVM.exploreAds.length >= 3 ? adVM.exploreAds.take(3).toList() : adVM.exploreAds,
isAdsFragment: false,
scrollPhysics: NeverScrollableScrollPhysics(),
),
],
)
]
],
);
},
),
20.height,
],
),
),

@ -1,3 +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';
@ -31,19 +32,23 @@ class ProvidersFragment extends StatelessWidget {
Expanded(
child: Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: 30,
itemBuilder: (BuildContext context, int index) {
return ProviderDetailsCard(
onCardTapped: () {
navigateWithName(context, AppRoutes.bookProviderAppView);
},
providerImageUrl: MyAssets.bnCar,
providerLocation: " 3km",
providerName: "Al Ahmed Maintenance",
providerRatings: "4.9",
);
}),
shrinkWrap: true,
itemCount: 30,
itemBuilder: (BuildContext context, int index) {
return ProviderDetailsCard(
onCardTapped: () {
if (context.read<AppointmentsVM>().providerCategories.isEmpty) {
context.read<AppointmentsVM>().getProviderCategories();
}
navigateWithName(context, AppRoutes.bookAppointmenServicesView);
},
providerImageUrl: MyAssets.bnCar,
providerLocation: " 3km",
providerName: "Al Ahmed Maintenance",
providerRatings: "4.9",
);
},
),
),
),
],

Loading…
Cancel
Save