Merge branch 'faiz_development' of http://34.17.52.79/Haroon6138/car_customer_app into faiz_development

mirza_development
devshafique 2 years ago
commit e79ad123fd

@ -1,12 +1,8 @@
import 'package:car_customer_app/repositories/provider_repo.dart';
import 'package:car_customer_app/repositories/appointment_repo.dart';
import 'package:mc_common_app/config/dependencies.dart';
class CustomerDependencies {
static void addDependencies() {
AppDependencies.addDependencies();
injector.registerSingleton<ProviderRepo>(() => ProviderRepoImp());
injector.registerSingleton<AppointmentRepo>(() => ScheduleRepoImp());
}
}

@ -1,9 +1,11 @@
import 'package:car_customer_app/views/appointments/appointment_detail_view.dart';
import 'package:car_customer_app/views/appointments/book_appointment_schedules_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:mc_common_app/models/requests_models/offers_model.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/views/appointments/appointment_detail_view.dart';
import 'package:mc_common_app/views/appointments/book_appointment_schedules_view.dart';
import 'package:mc_common_app/views/appointments/book_appointment_services_view.dart';
import 'package:mc_common_app/views/appointments/book_appointments_item_view.dart';
import 'package:mc_common_app/views/appointments/pick_items_view.dart';
import 'package:mc_common_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';
@ -18,8 +20,14 @@ 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_filter_view.dart';
import 'package:mc_common_app/views/requests/create_request_page.dart';
import 'package:mc_common_app/views/requests/offer_list_page.dart';
import 'package:mc_common_app/views/setting_options/setting_options_faqs.dart';
import 'package:mc_common_app/views/setting_options/setting_options_invite_friends.dart';
import 'package:mc_common_app/views/setting_options/setting_options_language.dart';
import 'package:mc_common_app/views/chat/chat_view.dart';
class CustomerAppRoutes {
class CustomerAppRoutes {
static final Map<String, WidgetBuilder> routes = {
AppRoutes.dashboard: (context) => DashboardPage(),
AppRoutes.bookProviderAppView: (context) => BookProviderAppView(),
@ -37,5 +45,13 @@ class CustomerAppRoutes {
AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(paymentType: ModalRoute.of(context)!.settings.arguments as PaymentTypes),
AppRoutes.branchDetailPage: (context) => BranchDetailPage(branchDetailModel: ModalRoute.of(context)!.settings.arguments as BranchDetailModel),
AppRoutes.providerProfilePage: (context) => ProviderProfilePage(providerId: ModalRoute.of(context)!.settings.arguments as int),
AppRoutes.offersListPage: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments),
AppRoutes.createRequestPage: (context) => CreateRequestPage(),
AppRoutes.chatView: (context) => ChatView(
chatViewArguments: ModalRoute.of(context)!.settings.arguments as ChatViewArguments,
),
AppRoutes.settingOptionsFaqs: (context) => SettingOptionsFAQs(),
AppRoutes.settingOptionsLanguages: (context) => SettingOptionsLanguage(),
AppRoutes.settingOptionsInviteFriends: (context) => SettingOptionsInviteFriends(),
};
}

@ -1,15 +1,19 @@
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/appointment_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:mc_common_app/repositories/chat_repo.dart';
import 'package:mc_common_app/repositories/provider_repo.dart';
import 'package:mc_common_app/repositories/appointment_repo.dart';
import 'package:mc_common_app/repositories/request_repo.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:easy_localization/easy_localization.dart';
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/models/post_params_model.dart';
import 'package:mc_common_app/models/general_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';
@ -38,8 +42,8 @@ Future<void> main() async {
MultiProvider(
providers: <SingleChildWidget>[
ChangeNotifierProvider<BaseVM>(create: (_) => BaseVM()),
ChangeNotifierProvider<DashboardVM>(
create: (_) => DashboardVM(
ChangeNotifierProvider<DashboardVmCustomer>(
create: (_) => DashboardVmCustomer(
commonServices: injector.get<CommonAppServices>(),
userRepo: injector.get<UserRepo>(),
),
@ -65,6 +69,14 @@ Future<void> main() async {
ChangeNotifierProvider<PaymentVM>(
create: (_) => PaymentVM(paymentService: injector.get<PaymentService>(), paymentRepo: injector.get<PaymentsRepo>()),
),
ChangeNotifierProvider<ChatVM>(create: (_) => ChatVM(chatRepo: injector.get<ChatRepo>())),
ChangeNotifierProvider<RequestsVM>(
create: (_) => RequestsVM(
requestRepo: injector.get<RequestRepo>(),
commonServices: injector.get<CommonAppServices>(),
commonRepo: injector.get<CommonRepo>(),
),
),
],
child: MyApp(),
).setupLocale(),

@ -1,159 +0,0 @@
import 'dart:developer';
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';
import 'package:mc_common_app/models/service_schedule_model.dart';
import 'package:mc_common_app/utils/enums.dart';
abstract class AppointmentRepo {
Future<Services> getAllServices(String branchId);
Future<MResponse> createSchedule(Map map);
Future<MResponse> addServicesInSchedule(Map map);
Future<MResponse> updateSchedule(Map map);
Future<List<ScheduleData>> getSchedules(String branchId);
Future<MResponse> updateServicesInSchedule(Map map);
Future<List<ServiceAppointmentScheduleModel>> mergeServiceIntoAvailableSchedules({
required List<String> serviceItemIdsForHome,
required List<String> serviceItemIdsForWorkshop,
});
Future<GenericRespModel> createServiceAppointment({required List<ServiceAppointmentScheduleModel> schedules, required int serviceProviderID});
Future<GenericRespModel> cancelOrRescheduleServiceAppointment({required int serviceAppointmentID, required int serviceSlotID, required int appointmentScheduleAction});
}
class ScheduleRepoImp implements AppointmentRepo {
@override
Future<Services> getAllServices(String branchId) async {
Map<String, dynamic> map = {"ProviderBranchID": branchId};
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().getJsonForObject((json) => Services.fromJson(json), ApiConsts.getServicesOfBranch, token: t, queryParameters: map);
}
@override
Future<MResponse> createSchedule(Map map) async {
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.createSchedule, map, token: t);
}
@override
Future<MResponse> addServicesInSchedule(Map map) async {
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.createGroup, map, token: t);
}
@override
Future<List<ScheduleData>> getSchedules(String branchId) async {
Map<String, dynamic> map = {"ServiceProviderBranchID": branchId};
String t = AppState().getUser.data!.accessToken ?? "";
GenericRespModel adsGenericModel = await injector.get<ApiClient>().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<MResponse> updateSchedule(Map map) async {
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateSchedule, map, token: t);
}
@override
Future<MResponse> updateServicesInSchedule(Map map) async {
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateGroup, map, token: t);
}
Future<List<ServiceAppointmentScheduleModel>> mergeServiceIntoAvailableSchedules({
required List<String> serviceItemIdsForHome,
required List<String> serviceItemIdsForWorkshop,
}) async {
String t = AppState().getUser.data!.accessToken ?? "";
var queryParameters = [
{
"appointmentType": 2,
"ServiceItemIDs": serviceItemIdsForHome,
},
{
"appointmentType": 1,
"ServiceItemIDs": serviceItemIdsForWorkshop,
}
];
GenericRespModel adsGenericModel = await injector.get<ApiClient>().postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.GetServiceItemAppointmentScheduleSlots,
queryParameters,
token: t,
);
List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleModel =
List.generate(adsGenericModel.data.length, (index) => ServiceAppointmentScheduleModel.fromJson(adsGenericModel.data[index], isForAppointment: true));
return serviceAppointmentScheduleModel;
}
Future<GenericRespModel> createServiceAppointment({required List<ServiceAppointmentScheduleModel> schedules, required int serviceProviderID}) async {
String t = AppState().getUser.data!.accessToken ?? "";
int customerId = AppState().getUser.data!.userInfo!.customerId ?? 0;
List<Map<String, dynamic>> mapList = [];
schedules.forEach((schedule) {
List<int> serviceItemIds = [];
schedule.servicesListInAppointment!.forEach((service) {
service.serviceItems!.forEach((item) {
serviceItemIds.add(item.id!);
});
});
mapList.add({
"serviceSlotID": schedule.selectedCustomTimeDateSlotModel!.date!.slotId,
"serviceProviderID": serviceProviderID,
"customerID": customerId,
"serviceItemID": serviceItemIds,
});
});
GenericRespModel adsGenericModel = await injector.get<ApiClient>().postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.ServiceProvidersAppointmentCreate,
mapList,
token: t,
);
return adsGenericModel;
}
@override
Future<GenericRespModel> cancelOrRescheduleServiceAppointment({required int serviceAppointmentID, required int serviceSlotID, required int appointmentScheduleAction}) async {
String t = AppState().getUser.data!.accessToken ?? "";
final payload = {
"serviceAppointmentID": serviceAppointmentID,
"serviceSlotID": serviceSlotID,
"appointmentScheduleAction": appointmentScheduleAction,
};
GenericRespModel adsGenericModel = await injector.get<ApiClient>().postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.ServiceProviderAppointmentRescheduleCancelAppointment,
payload,
token: t,
);
return adsGenericModel;
}
}

@ -1,55 +0,0 @@
import 'dart:async';
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/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 {
Future<List<BranchDetailModel>> getAllNearBranchAndServices();
Future<List<ItemData>> getServiceItems(int serviceId);
Future<ProviderProfileModel> getBranchAndServices(int providerId);
}
class ProviderRepoImp implements ProviderRepo {
ApiClient apiClient = injector.get<ApiClient>();
AppState appState = injector.get<AppState>();
@override
Future<List<BranchDetailModel>> getAllNearBranchAndServices() async {
GenericRespModel adsGenericModel = await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.GetAllNearBranches, token: appState.getUser.data!.accessToken);
List<BranchDetailModel> nearBranches = List.generate(adsGenericModel.data.length, (index) => BranchDetailModel.fromJson(adsGenericModel.data[index]));
return nearBranches;
}
@override
Future<List<ItemData>> getServiceItems(int serviceId) async {
var queryParameters = {
"ServiceProviderServiceID": serviceId.toString(),
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.getServiceItems,
token: appState.getUser.data!.accessToken,
queryParameters: queryParameters,
);
List<ItemData> serviceItems = List.generate(adsGenericModel.data.length, (index) => ItemData.fromJson(adsGenericModel.data[index]));
return serviceItems;
}
@override
Future<ProviderProfileModel> getBranchAndServices(int providerId) async {
var postParams = {"serviceProviderID": providerId.toString()};
GenericRespModel adsGenericModel =
await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.BranchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams);
return ProviderProfileModel.fromJson(adsGenericModel.data);
}
}

@ -1,673 +0,0 @@
import 'package:car_customer_app/repositories/provider_repo.dart';
import 'package:car_customer_app/repositories/appointment_repo.dart';
import 'package:car_customer_app/view_models/dashboard_view_model.dart';
import 'package:car_customer_app/views/appointments/book_appointment_schedules_view.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/appointments_models/appointment_list_model.dart';
import 'package:mc_common_app/models/generic_resp_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/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/repositories/common_repo.dart';
import 'package:mc_common_app/services/common_services.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/utils/utils.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/widgets/common_widgets/info_bottom_sheet.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 AppointmentsVM extends BaseVM {
final CommonRepo commonRepo;
final CommonAppServices commonServices;
final ProviderRepo providerRepo;
final AppointmentRepo scheduleRepo;
AppointmentsVM({required this.commonServices, required this.scheduleRepo, required this.providerRepo, required this.commonRepo});
bool isFetchingLists = false;
List<AppointmentListModel> myAppointments = [];
List<AppointmentListModel> myUpComingAppointments = [];
List<AppointmentListModel> myFilteredAppointments = [];
List<FilterListModel> appointmentsFilterOptions = [];
// List<ScheduleData> availableSchedules = [];
bool isFetchingServices = false;
List<DropValue> branchCategories = [];
bool isHomeTapped = false;
List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleList = [];
bool ifItemAlreadySelected(int id) {
int indexFound = allSelectedItemsInAppointments.indexWhere((element) => element.id == id);
if (indexFound != -1) {
return true;
}
return false;
}
List<ItemData> allSelectedItemsInAppointments = [];
Future<void> onItemsSelectedInService() async {
if (currentServiceSelection != null) {
int index = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!);
if (index == -1) {
double totalPrice = 0.0;
currentServiceSelection!.serviceItems!.forEach((element) {
totalPrice = totalPrice + double.parse(element.price ?? "0.0");
});
currentServiceSelection!.currentTotalServicePrice = totalPrice;
servicesInCurrentAppointment.insert(0, currentServiceSelection!);
}
resetCategorySelectionBottomSheet();
notifyListeners();
}
}
Future<void> onBookAppointmentPressed(BuildContext context) async {
Utils.showLoading(context);
bool isSuccess = false;
List<int> appointmentIdsList = [];
try {
GenericRespModel genericRespModel = await scheduleRepo.createServiceAppointment(
schedules: serviceAppointmentScheduleList,
serviceProviderID: selectedBranchModel!.serviceProviderId ?? 0,
);
if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
Utils.hideLoading(context);
Utils.showToast("${genericRespModel.message.toString()}");
return;
}
if (genericRespModel.data != null) {
genericRespModel.data.forEach((element) {
if (element['appointmentID'] != 0) {
appointmentIdsList.add(element['appointmentID']);
isSuccess = true;
} else {
isSuccess = false;
return;
}
});
}
context.read<DashboardVM>().onNavbarTapped(1);
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.booked);
Utils.hideLoading(context);
resetAfterBookingAppointment();
if (isSuccess) {
if (amountToPayForAppointment > 0) {
context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: appointmentIdsList);
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment);
} else {
Utils.showToast("Your appointment has been booked successfully!");
getMyAppointments();
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
}
} catch (e) {
Utils.showToast("${e.toString()}");
}
}
Future<void> onConfirmAppointmentPressed({required BuildContext context, required appointmentId}) async {
context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: [appointmentId]);
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment);
}
Future<void> onCancelAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async {
Utils.showLoading(context);
try {
GenericRespModel genericRespModel = await scheduleRepo.cancelOrRescheduleServiceAppointment(
serviceAppointmentID: appointmentListModel.id ?? 0,
serviceSlotID: appointmentListModel.serviceSlotID ?? 0,
appointmentScheduleAction: 2, // 1 for Reschedule and 2 for Cancel
);
if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
Utils.hideLoading(context);
Utils.showToast("${genericRespModel.message.toString()}");
return;
}
if (genericRespModel.data == 1) {
context.read<DashboardVM>().onNavbarTapped(1);
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
Utils.showToast("${genericRespModel.message.toString()}");
await getMyAppointments();
Utils.hideLoading(context);
getMyAppointments();
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
} catch (e) {
Utils.showToast("${e.toString()}");
}
}
void updateIsHomeTapped(bool value) {
isHomeTapped = value;
if (currentServiceSelection != null) {
currentServiceSelection!.isHomeSelected = value;
}
notifyListeners();
}
String pickedHomeLocation = "";
void updatePickedHomeLocation(String value) {
pickedHomeLocation = value;
pickHomeLocationError = "";
if (currentServiceSelection != null) {
currentServiceSelection!.homeLocation = value;
}
notifyListeners();
}
SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
void updateProviderCategoryId(SelectionModel id) {
branchSelectedCategoryId = id;
getBranchServices(categoryId: branchSelectedCategoryId.selectedId);
notifyListeners();
}
List<FilterListModel> providersFilterOptions = [];
List<BranchDetailModel> nearbyBranches = [];
BranchDetailModel? selectedBranchModel;
List<ServiceModel> branchServices = [];
List<ServiceModel> servicesInCurrentAppointment = [];
ServiceModel? currentServiceSelection;
void updateBranchServiceId(SelectionModel id) async {
branchSelectedServiceId = id;
currentServiceSelection = branchServices.firstWhere((element) => element.serviceProviderServiceId == id.selectedId);
notifyListeners();
}
resetCategorySelectionBottomSheet() {
selectedSubServicesCounter = 0;
branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
isHomeTapped = false;
branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
currentServiceSelection = null;
}
resetAfterBookingAppointment() {
servicesInCurrentAppointment.clear();
allSelectedItemsInAppointments.clear();
serviceAppointmentScheduleList.clear();
}
populateAppointmentsFilterList() {
appointmentsFilterOptions.clear();
appointmentsFilterOptions = [
FilterListModel(title: "All Appointments", isSelected: true, id: -1),
FilterListModel(title: "Booked", isSelected: false, id: 1),
FilterListModel(title: "Confirmed", isSelected: false, id: 2),
FilterListModel(title: "Arrived", isSelected: false, id: 3),
FilterListModel(title: "Cancelled", isSelected: false, id: 4),
];
notifyListeners();
}
applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum}) {
if (appointmentsFilterOptions.isEmpty) return;
for (var value in appointmentsFilterOptions) {
value.isSelected = false;
}
appointmentsFilterOptions[appointmentStatusEnum.getIdFromAppointmentStatusEnum() == -1 ? 0 : appointmentStatusEnum.getIdFromAppointmentStatusEnum()].isSelected = true;
if (appointmentStatusEnum.getIdFromAppointmentStatusEnum() == -1) {
myFilteredAppointments = myAppointments;
notifyListeners();
return;
}
myFilteredAppointments = myAppointments.where((element) => element.appointmentStatusID! == appointmentStatusEnum.getIdFromAppointmentStatusEnum()).toList();
notifyListeners();
}
Future<void> getMyAppointments() async {
isFetchingLists = true;
myAppointments = await commonRepo.getMyAppointments();
myFilteredAppointments = myAppointments;
myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.booked).toList();
// applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
isFetchingLists = false;
notifyListeners();
}
updateSelectedBranch(BranchDetailModel branchDetailModel) {
selectedBranchModel = branchDetailModel;
getBranchCategories();
notifyListeners();
}
updateSelectedAppointmentDate({required int dateIndex, required int scheduleIndex}) {
serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!.forEach((element) {
element.date!.isSelected = false;
});
serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.isSelected = true;
serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex = dateIndex;
final date = TimeSlotModel(
date: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.date,
slotId: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.slotId,
isSelected: true,
slot: "",
);
serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel = CustomTimeDateSlotModel(date: date);
notifyListeners();
}
updateSelectedAppointmentSlotByDate({required int scheduleIndex, required int slotIndex}) {
serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!.forEach((element) {
element.availableSlots!.forEach((element) => element.isSelected = false);
});
int index = serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!;
serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots![slotIndex].isSelected = true;
serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel!.availableSlots = serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots!;
notifyListeners();
}
double amountToPayForAppointment = 0.0;
double totalAmount = 0.0;
List<ItemData> serviceItemsFromApi = [];
ProviderProfileModel? providerProfileModel;
int selectedSubServicesCounter = 0;
onItemUpdateOrSelected(int index, bool selected, int itemId) {
int serviceIndex = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!);
serviceItemsFromApi[index].isUpdateOrSelected = selected;
serviceItemsFromApi[index].isHomeSelected = isHomeTapped;
if (selected) {
selectedSubServicesCounter = selectedSubServicesCounter + 1;
selectSubServicesError = "";
currentServiceSelection!.serviceItems!.add(serviceItemsFromApi[index]);
allSelectedItemsInAppointments.add(serviceItemsFromApi[index]);
allSelectedItemsInAppointments.forEach((element) {
if (!ifItemAlreadySelected(element.id!)) {
servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]);
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0");
}
});
}
if (!selected) {
selectedSubServicesCounter = selectedSubServicesCounter - 1;
currentServiceSelection!.serviceItems!.removeWhere((element) => element.id == itemId);
allSelectedItemsInAppointments.removeWhere((element) => element.id == itemId);
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0");
servicesInCurrentAppointment[serviceIndex].serviceItems!.removeWhere((element) => element.id == itemId);
}
notifyListeners();
}
populateBranchesFilterList() {
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();
}
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<List<ItemData>> getServiceItems(int serviceId) async {
serviceItemsFromApi.clear();
serviceItemsFromApi = await providerRepo.getServiceItems(serviceId);
serviceItemsFromApi.forEach((item) {
if (ifItemAlreadySelected(item.id!)) {
item.isUpdateOrSelected = true;
}
});
setState(ViewState.idle);
return serviceItemsFromApi;
}
getBranchAndServices(int providerId) async {
providerProfileModel = null;
providerProfileModel = await providerRepo.getBranchAndServices(providerId);
setState(ViewState.idle);
}
String pickHomeLocationError = "";
String selectSubServicesError = "";
SelectionModel branchSelectedServiceId = 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 selectedBranchModel!.branchServices!) {
if (!isCategoryAlreadyPresent(value.categoryId!)) {
branchCategories.add(DropValue(value.categoryId!, value.categoryName!, ""));
}
}
notifyListeners();
}
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);
isFetchingServices = false;
notifyListeners();
}
}
List<ServiceModel> getFilteredBranchServices({required int categoryId}) {
List<ServiceModel> filteredServices = selectedBranchModel!.branchServices!.where((element) => element.categoryId == categoryId).toList();
return filteredServices;
}
void updatePickHomeLocationError(String value) {
pickHomeLocationError = value;
// notifyListeners();
}
bool isServiceSelectionValidated() {
if (branchSelectedServiceId.selectedId == -1) {
return false;
}
if (isHomeTapped) {
if (pickedHomeLocation == "") {
updatePickHomeLocationError(GlobalConsts.homeLocationEmptyError);
return false;
}
}
return true;
}
bool validateItemsSelection() {
for (var value in serviceItemsFromApi) {
if (value.isUpdateOrSelected!) {
return true;
}
}
selectSubServicesError = "Please select at least one sub service";
notifyListeners();
return false;
}
String getTotalPrice(List<ServiceModel> serviceItems) {
var totalPrice = 0.0;
serviceItems.forEach((element) {
totalPrice = totalPrice + (element.currentTotalServicePrice);
});
return totalPrice.toString();
}
void openTheAddServiceBottomSheet(BuildContext context, AppointmentsVM appointmentsVM) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return AppointmentServicePickBottomSheet();
},
);
}
void priceBreakDownClicked(BuildContext context, ServiceModel selectedService) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
double totalKms = 15.3;
return InfoBottomSheet(
title: "Charges Breakdown".toText(fontSize: 24, isBold: true),
description: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Services".toText(fontSize: 16, isBold: true),
Column(
children: List.generate(
selectedService.serviceItems!.length,
(index) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"${selectedService.serviceItems![index].name}".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"${selectedService.serviceItems![index].price} SAR".toText(fontSize: 12, isBold: true),
],
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
"${selectedService.currentTotalServicePrice} SAR".toText(fontSize: 16, isBold: true),
],
),
if (selectedService.isHomeSelected) ...[
20.height,
"Home Location".toText(fontSize: 16, isBold: true),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"${totalKms}km ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"${selectedService.rangePricePerKm} x $totalKms".toText(fontSize: 12, isBold: true),
],
),
8.height,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
"${selectedService.rangePricePerKm ?? 0 * totalKms} 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: [
(selectedService.isHomeSelected
? "${(selectedService.currentTotalServicePrice) + (double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms)}"
: "${selectedService.currentTotalServicePrice}")
.toText(fontSize: 29, isBold: true),
2.width,
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
],
)
],
),
30.height,
],
));
});
}
void onReviewButtonPressed(BuildContext context) {
bool isValidated = false;
for (int i = 0; i < serviceAppointmentScheduleList.length; i++) {
final schedule = serviceAppointmentScheduleList[i];
if (schedule.selectedCustomTimeDateSlotModel == null) {
isValidated = false;
break;
}
if (schedule.selectedCustomTimeDateSlotModel!.date == null || !schedule.selectedCustomTimeDateSlotModel!.date!.isSelected) {
isValidated = false;
break;
} else {
if (schedule.selectedCustomTimeDateSlotModel!.availableSlots == null) {
isValidated = true;
break;
} else {
TimeSlotModel slot = schedule.selectedCustomTimeDateSlotModel!.availableSlots!.firstWhere((element) => element.isSelected);
if (slot.date.isNotEmpty) {
isValidated = true;
break;
}
}
}
}
if (!isValidated) {
Utils.showToast("You must select appointment time for each schedule's appointment.");
return;
}
navigateWithName(context, AppRoutes.reviewAppointmentView);
}
void onServicesNextPressed(BuildContext context) async {
Utils.showLoading(context);
List<String> serviceItemIdsForHome = [];
List<String> serviceItemIdsForWorkshop = [];
allSelectedItemsInAppointments.forEach((serviceItem) {
if (serviceItem.isHomeSelected!) {
serviceItemIdsForHome.add(serviceItem.id!.toString());
} else {
serviceItemIdsForWorkshop.add(serviceItem.id!.toString());
}
});
serviceAppointmentScheduleList = await scheduleRepo.mergeServiceIntoAvailableSchedules(
serviceItemIdsForHome: serviceItemIdsForHome,
serviceItemIdsForWorkshop: serviceItemIdsForWorkshop,
);
if (serviceAppointmentScheduleList.isEmpty) {
Utils.hideLoading(context);
Utils.showToast("There are no available appointments for selected Items.");
return;
}
totalAmount = 0.0;
amountToPayForAppointment = 0.0;
serviceAppointmentScheduleList.forEach(
(schedule) {
amountToPayForAppointment = amountToPayForAppointment + (schedule.amountToPay ?? 0.0);
totalAmount = totalAmount + (schedule.amountTotal ?? 0.0);
},
);
Utils.hideLoading(context);
navigateWithName(context, AppRoutes.bookAppointmenSchedulesView, arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 1, appointmentId: 0)); // 1 For Creating an Appointment
notifyListeners();
}
Future<void> onRescheduleAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async {
Utils.showLoading(context);
List<String> serviceItemIdsForHome = [];
List<String> serviceItemIdsForWorkshop = [];
appointmentListModel.appointmentServicesList!.forEach((service) {
service.serviceItems!.forEach((serviceItem) {
serviceItemIdsForWorkshop.add(serviceItem.id!.toString());
// if (serviceItem.isHomeSelected ?? false) {
// serviceItemIdsForHome.add(serviceItem.id!.toString());
// } else {
// serviceItemIdsForWorkshop.add(serviceItem.id!.toString());
// }
});
});
serviceAppointmentScheduleList = await scheduleRepo.mergeServiceIntoAvailableSchedules(
serviceItemIdsForHome: serviceItemIdsForHome,
serviceItemIdsForWorkshop: serviceItemIdsForWorkshop,
);
if (serviceAppointmentScheduleList.isEmpty) {
Utils.hideLoading(context);
Utils.showToast("There are no available appointments for selected Items.");
return;
}
Utils.hideLoading(context);
navigateWithName(context, AppRoutes.bookAppointmenSchedulesView,
arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 2, appointmentId: appointmentListModel.id ?? 0)); // 2 For Rescheduling an Appointment
notifyListeners();
}
Future<void> onRescheduleAppointmentConfirmPressed({required BuildContext context, required int appointmentId, required int selectedSlotId}) async {
Utils.showLoading(context);
try {
GenericRespModel genericRespModel = await scheduleRepo.cancelOrRescheduleServiceAppointment(
serviceAppointmentID: appointmentId,
serviceSlotID: selectedSlotId,
appointmentScheduleAction: 1, // 1 for Reschedule and 2 for Cancel
);
if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
Utils.hideLoading(context);
Utils.showToast("${genericRespModel.message.toString()}");
return;
}
if (genericRespModel.data == 1) {
context.read<DashboardVM>().onNavbarTapped(1);
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
Utils.showToast("${genericRespModel.message.toString()}");
getMyAppointments();
Utils.hideLoading(context);
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
} catch (e) {
Utils.showToast("${e.toString()}");
}
}
}

@ -1,55 +0,0 @@
import 'dart:io';
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/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 CommonAppServices commonServices;
final UserRepo userRepo;
DashboardVM({required this.commonServices, required this.userRepo});
String pickedImage = "";
int selectedNavbarBarIndex = 2;
void onNavbarTapped(int index) {
selectedNavbarBarIndex = index;
notifyListeners();
}
void pickImageFromPhone(BuildContext context, int sourceFlag) async {
final File? pickedImageFile = await commonServices.pickImageFromPhone(sourceFlag);
if (pickedImageFile == null) {
return;
}
int sizeInBytes = pickedImageFile.lengthSync();
if (sizeInBytes > 1000) {
Utils.showToast(LocaleKeys.fileLarger.tr());
return;
} else {
String image64 = Utils.convertFileToBase64(pickedImageFile);
Utils.showLoading(context);
ImageResponse response = await userRepo.updateUserImage(image64);
Utils.hideLoading(context);
Navigator.pop(context);
if (response.messageStatus == 1) {
Utils.showToast(LocaleKeys.imageUploaded.tr());
AppState().getUser.data!.userInfo!.userImageUrl = response.data;
} else {
Utils.showToast(response.message ?? "");
}
}
}
Future<ImageResponse> updateUserImage(String image) async {
return await userRepo.updateUserImage(image);
}
}

@ -1,229 +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/appointments_models/appointment_list_model.dart';
import 'package:mc_common_app/models/services/service_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.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/card_button_with_icon.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class AppointmentDetailView extends StatelessWidget {
final AppointmentListModel appointmentListModel;
AppointmentDetailView({Key? key, required this.appointmentListModel}) : super(key: key);
Widget getBaseActionButtonWidget({required Color color, required String text, Color textColor = MyColors.white, required Function() onPressed}) {
return Expanded(
child: ShowFillButton(
maxHeight: 55,
title: text,
onPressed: onPressed,
backgroundColor: color,
txtColor: textColor,
fontSize: 18,
),
);
}
Widget buildBottomActionButton({required AppointmentStatusEnum appointmentStatusEnum, required BuildContext context}) {
switch (appointmentStatusEnum) {
case AppointmentStatusEnum.booked:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"),
12.width,
getBaseActionButtonWidget(
color: MyColors.greenColor,
onPressed: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
},
text: "Confirm"),
],
),
);
case AppointmentStatusEnum.confirmed:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"),
],
),
);
case AppointmentStatusEnum.arrived:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(color: MyColors.grey98Color.withOpacity(0.3), textColor: MyColors.lightTextColor, onPressed: () {}, text: "In Progress"),
],
),
);
case AppointmentStatusEnum.cancelled:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(color: MyColors.grey98Color.withOpacity(0.3), textColor: MyColors.lightTextColor, onPressed: () {}, text: "Cancelled"),
],
),
);
case AppointmentStatusEnum.allAppointments:
return SizedBox();
}
}
void appointmentCancelConfirmationSheet(BuildContext context) {
final appointmentsVm = context.read<AppointmentsVM>();
return actionConfirmationBottomSheet(
context: context,
title: "Do you want to cancel this appointment?".toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: "Your appointment will be cancelled and you cannot undo this action.",
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: "Yes",
fontSize: 15,
onPressed: () {
Navigator.pop(context);
appointmentsVm.onCancelAppointmentPressed(context: context, appointmentListModel: appointmentListModel);
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: "No",
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
@override
Widget build(BuildContext context) {
AppointmentsVM appointmentsVM = context.read<AppointmentsVM>();
return Scaffold(
appBar: CustomAppBar(
title: "Appointment",
profileImageUrl: MyAssets.bnCar,
isRemoveBackButton: false,
isDrawerEnabled: false,
),
body: Container(
padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
child: Stack(
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
appointmentListModel.providerName!.toText(fontSize: 16, isBold: true),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
MyAssets.miniClockDark.buildSvg(
height: 12,
width: 12,
fit: BoxFit.fill,
),
5.width,
"${appointmentListModel.duration ?? ""} ${appointmentListModel.appointmentDate!.toFormattedDateWithoutTime()}".toText(fontSize: 12, isBold: true, color: MyColors.lightTextColor),
],
),
13.height,
if (appointmentListModel.appointmentServicesList != null && appointmentListModel.appointmentServicesList!.isNotEmpty) ...[
Column(
children: List.generate(appointmentListModel.appointmentServicesList!.length, (index) {
ServiceModel service = appointmentListModel.appointmentServicesList![index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
// MyAssets.maintenanceIcon.buildSvg(
// height: 10,
// width: 10,
// fit: BoxFit.fill,
// ),
// 10.width,
"${index + 1}. ${service.providerServiceDescription}".toText(fontSize: 14, isBold: true),
],
),
if (service.serviceItems != null && service.serviceItems!.isNotEmpty) ...[
Column(
children: List.generate(
service.serviceItems!.length,
(index) => "${service.serviceItems![index].name}".toText(
textAlign: TextAlign.start,
fontSize: 12,
color: MyColors.lightTextColor,
),
),
).paddingOnly(left: 15),
],
5.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
((service.currentTotalServicePrice).toString()).toText(fontSize: 25, isBold: true),
2.width,
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
Icon(
Icons.arrow_drop_down,
size: 30,
)
],
).onPress(() => appointmentsVM.priceBreakDownClicked(context, service)),
],
);
}),
),
],
15.height,
Row(
children: [
CardButtonWithIcon(
title: "Reschedule Appointment",
onCardTapped: () {
context.read<AppointmentsVM>().onRescheduleAppointmentPressed(context: context, appointmentListModel: appointmentListModel);
},
icon: MyAssets.scheduleAppointmentIcon.buildSvg(),
),
if (appointmentListModel.appointmentStatusEnum == AppointmentStatusEnum.booked) ...[
10.width,
CardButtonWithIcon(
title: "Pay for Appointment",
onCardTapped: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
},
icon: MyAssets.creditCardIcon.buildSvg(),
),
],
],
),
15.height,
],
).toWhiteContainer(width: double.infinity, allPading: 12),
buildBottomActionButton(appointmentStatusEnum: appointmentListModel.appointmentStatusEnum!, context: context),
],
),
),
);
}
}

@ -1,159 +0,0 @@
import 'package:car_customer_app/view_models/appointments_view_model.dart';
import 'package:car_customer_app/views/appointments/widgets/custom_calender_widget.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/service_schedule_model.dart';
import 'package:mc_common_app/models/services/service_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/common_widgets/time_slots.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class ScreenArgumentsForAppointmentDetailPage {
final int routeFlag; // 1 = coming from create appointment || 2 = coming from reschedule appointment
final int appointmentId; // 1 = coming from create appointment || 2 = coming from reschedule appointment
ScreenArgumentsForAppointmentDetailPage({required this.routeFlag, required this.appointmentId});
}
class BookAppointmentSchedulesView extends StatelessWidget {
final ScreenArgumentsForAppointmentDetailPage screenArgumentsForAppointmentDetailPage;
BookAppointmentSchedulesView({Key? key, required this.screenArgumentsForAppointmentDetailPage}) : super(key: key);
@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,
ListView.builder(
shrinkWrap: true,
itemCount: appointmentsVM.serviceAppointmentScheduleList.length,
itemBuilder: (BuildContext context, int scheduleIndex) {
ServiceAppointmentScheduleModel scheduleData = appointmentsVM.serviceAppointmentScheduleList[scheduleIndex];
return ExpansionTile(
tilePadding: EdgeInsets.symmetric(horizontal: 21, vertical: 10),
childrenPadding: EdgeInsets.only(left: 16, bottom: 10, right: 16),
title: Column(
children: [
Row(
children: [
Expanded(
child: "Schedule ${scheduleIndex + 1}".toText(fontSize: 20, isBold: true),
),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Service Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
("${scheduleData.appointmentType == 2 ? "Home" : "Workshop"}").toText(fontSize: 12, isBold: true).expand(),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
5.height,
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].servicesListInAppointment!.length,
itemBuilder: (BuildContext context, int serviceIndex) {
ServiceModel selectedService = appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].servicesListInAppointment![serviceIndex];
return Row(
children: [
Expanded(
child: ("${serviceIndex + 1}. ${selectedService.providerServiceDescription}").toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor),
),
],
);
},
),
],
),
],
),
children: [
Column(
children: [
CustomCalenderWidget(customTimeDateSlotList: scheduleData.customTimeDateSlotList ?? [], scheduleIndex: scheduleIndex),
if (appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex != null) ...[
5.height,
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
("Available Slots").toText(fontSize: 14, isBold: true),
],
),
5.height,
SizedBox(
width: double.infinity,
child: BuildTimeSlots(
timeSlots: appointmentsVM.serviceAppointmentScheduleList[scheduleIndex]
.customTimeDateSlotList![appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!].availableSlots ??
[],
onPressed: (slotIndex) {
appointmentsVM.updateSelectedAppointmentSlotByDate(scheduleIndex: scheduleIndex, slotIndex: slotIndex);
},
),
),
],
],
),
],
).toWhiteContainer(width: double.infinity, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 10));
},
).expand(),
Row(
children: [
Expanded(
child: ShowFillButton(
txtColor: MyColors.black,
maxHeight: 55,
title: "Cancel",
onPressed: () => Navigator.pop(context),
backgroundColor: MyColors.greyButtonColor,
),
),
12.width,
Expanded(
child: ShowFillButton(
maxHeight: 55,
title: screenArgumentsForAppointmentDetailPage.routeFlag == 1 ? "Review" : "Confirm",
onPressed: () {
if (screenArgumentsForAppointmentDetailPage.routeFlag == 1) {
appointmentsVM.onReviewButtonPressed(context);
} else {
appointmentsVM.onRescheduleAppointmentConfirmPressed(
context: context,
appointmentId: screenArgumentsForAppointmentDetailPage.appointmentId,
selectedSlotId: appointmentsVM.serviceAppointmentScheduleList.first.selectedCustomTimeDateSlotModel!.date!.slotId,
);
}
},
backgroundColor: MyColors.darkPrimaryColor,
),
)
],
).paddingAll(21)
],
);
},
));
}
}

@ -1,133 +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/services/item_model.dart';
import 'package:mc_common_app/models/services/service_model.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/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class BookAppointmentServicesView extends StatelessWidget {
BookAppointmentServicesView({Key? key}) : super(key: key);
@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(
needsBorder: true,
bgColor: MyColors.white,
onTap: () => appointmentsVM.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: appointmentsVM.servicesInCurrentAppointment.length,
itemBuilder: (BuildContext context, int serviceIndex) {
ServiceModel serviceData = appointmentsVM.servicesInCurrentAppointment[serviceIndex];
return Column(
children: [
Row(
children: [
Expanded(
child: (serviceData.serviceDescription ?? "").toText(fontSize: 15, isBold: true),
),
],
),
if (true) ...[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Service Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
(serviceData.isHomeSelected ? serviceData.homeLocation : "Workshop").toText(fontSize: 12, isBold: true).expand(),
],
),
5.height,
Column(
children: List.generate(serviceData.serviceItems!.length, (itemIndex) {
ItemData itemData = serviceData.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: [
((appointmentsVM.servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice).toString()).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(() => appointmentsVM.priceBreakDownClicked(context, appointmentsVM.servicesInCurrentAppointment[serviceIndex])),
],
],
).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: () {
appointmentsVM.servicesInCurrentAppointment.clear();
appointmentsVM.allSelectedItemsInAppointments.clear();
Navigator.pop(context);
},
backgroundColor: MyColors.greyButtonColor,
),
),
12.width,
Expanded(
child: ShowFillButton(
maxHeight: 55,
title: "Next",
onPressed: () {
appointmentsVM.onServicesNextPressed(context);
},
backgroundColor: MyColors.darkPrimaryColor,
),
)
],
).paddingAll(21)
],
);
},
));
}
}

@ -1,152 +0,0 @@
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';
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';
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: "${appointmentsVM.selectedSubServicesCounter} Item(s) 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(),
appointmentsVM.serviceItemsFromApi.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.serviceItemsFromApi.length,
itemBuilder: (BuildContext context, int index) {
ItemData itemData = appointmentsVM.serviceItemsFromApi[index];
return ServiceItemWithPriceCheckBox(
description: itemData.description ?? "Some description about the sub-services",
title: itemData.name!,
isSelected: itemData.isUpdateOrSelected!,
onSelection: (bool value) {
print("itemId: ${itemData.id}");
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,
),
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(
child: ShowFillButton(
txtColor: MyColors.black,
maxHeight: 55,
title: "Cancel",
onPressed: () {
appointmentsVM.resetCategorySelectionBottomSheet();
pop(context);
},
backgroundColor: MyColors.greyButtonColor,
),
),
12.width,
Expanded(
child: Builder(
builder: (BuildContext context) {
return ShowFillButton(
maxHeight: 55,
title: "Next",
onPressed: () async {
bool resp = appointmentsVM.validateItemsSelection();
if (resp) {
appointmentsVM.onItemsSelectedInService();
Navigator.pop(context);
}
// bool resp = appointmentsVM.validateItemsSelection();
// if (resp) {
// Utils.showLoading(context);
// await appointmentsVM.mergeServiceIntoAvailableSchedules();
// appointmentsVM.resetCategorySelectionBottomSheet();
// Utils.hideLoading(context);
// Navigator.of(context).pushReplacementNamed(AppRoutes.bookAppointmenServicesView);
// }
},
backgroundColor: !appointmentsVM.isServiceSelectionValidated() ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.darkPrimaryColor,
);
},
),
),
],
).horPaddingMain(),
16.height,
],
),
],
);
},
));
}
}

@ -1,168 +0,0 @@
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/common_widgets/provider_details_card.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';
class BookProviderAppView extends StatefulWidget {
const BookProviderAppView({Key? key}) : super(key: key);
@override
State<BookProviderAppView> createState() => _BookProviderAppViewState();
}
class _BookProviderAppViewState extends State<BookProviderAppView> {
bool isReview = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: "Appointment",
profileImageUrl: MyAssets.bnCar,
isRemoveBackButton: false,
isDrawerEnabled: false,
),
body: Container(
color: MyColors.backgroundColor,
width: double.infinity,
height: double.infinity,
child: Column(
children: [
Expanded(
child: ListView(
children: [
ProviderDetailsCard(
onCardTapped: () {},
providerImageUrl: MyAssets.bnCar,
providerLocation: "3km",
title: "Al Ahmed Maintenance",
providerRatings: "4.9",
),
12.height,
isReview ? ReviewAppointmentSection() : ServicesSelectionSection(),
10.height,
],
),
),
10.height,
Padding(
padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
child: ShowFillButton(
title: "Book Appointment",
maxWidth: double.infinity,
onPressed: () {
isReview = !isReview;
setState(() {});
},
),
),
],
),
),
);
}
}
class ServicesSelectionSection extends StatelessWidget {
const ServicesSelectionSection({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
List<DropValue> dropList = [
DropValue(0, "Maintenance", ""),
DropValue(1, "Car Wash", ""),
DropValue(2, "Monthly Checkup", ""),
DropValue(3, "Friendly Visit", ""),
DropValue(4, "Muftaa", ""),
];
return Container(
padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Select services you want".toText(fontSize: 18, isBold: true),
8.height,
DropdownField(
(DropValue value) {},
list: dropList,
hint: "Select service type",
),
8.height,
DropdownField(
(DropValue value) {},
list: dropList,
hint: "Select service type",
),
8.height,
DropdownField(
(DropValue value) {},
list: dropList,
hint: "Select service type",
),
22.height,
"Select date and time".toText(fontSize: 18, isBold: true),
8.height,
DropdownField(
(DropValue value) {},
list: dropList,
hint: "Select service type",
),
22.height,
"Available slots".toText(fontSize: 15, isBold: true),
8.height,
BuildTimeSlots(onPressed: (index) {}, timeSlots: []),
22.height,
"Total Amount".toText(fontSize: 18, isBold: true),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"3000".toText(fontSize: 20, isBold: true),
"SAR".toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor),
],
),
10.height,
],
).toWhiteContainer(width: double.infinity, allPading: 12),
);
}
}
class ReviewAppointmentSection extends StatelessWidget {
const ReviewAppointmentSection({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Review Appointment".toText(fontSize: 18, isBold: true),
15.height,
"Services".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
"Car Engine Check".toText(fontSize: 18, isBold: true),
13.height,
"Date and Time".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
"2 Feb, 2023 at 09:00am".toText(fontSize: 18, isBold: true),
13.height,
"Total Amount".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"3000".toText(fontSize: 20, isBold: true),
"SAR".toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor),
],
),
100.height,
],
).toWhiteContainer(width: double.infinity, allPading: 12),
);
}
}

@ -1,307 +0,0 @@
import 'dart:developer';
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/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/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 BuildContext context}) {
AppointmentsVM appointmentsVM = context.read<AppointmentsVM>();
return Padding(
padding: const EdgeInsets.only(
bottom: 10,
left: 21,
right: 21,
),
child: Row(
children: [
Image.asset(
MyAssets.bnCar,
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: [
(appointmentsVM.selectedBranchModel!.branchName ?? "").toText(fontSize: 16, isBold: true),
Row(
children: [
LocaleKeys.location.tr().toText(color: MyColors.lightTextColor, fontSize: 12),
2.width,
": ${appointmentsVM.selectedBranchModel!.branchDescription ?? ""}".toText(fontSize: 12),
],
),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
"4.9".toText(
isUnderLine: true,
isBold: true,
fontSize: 12,
),
2.width,
MyAssets.starIcon.buildSvg(width: 12),
],
),
],
),
],
),
),
],
).onPress(() {}).toWhiteContainer(width: double.infinity, allPading: 12));
}
Widget buildServicesInfoCard({required BuildContext context}) {
AppointmentsVM appointmentsVM = context.read<AppointmentsVM>();
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: "Services".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.separated(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: scheduleData.servicesListInAppointment!.length,
itemBuilder: (BuildContext context, int serviceIndex) {
String selectedTimeSlot = "";
if (scheduleData.selectedCustomTimeDateSlotModel!.availableSlots != null) {
selectedTimeSlot = scheduleData.selectedCustomTimeDateSlotModel!.availableSlots!.firstWhere((element) => element.isSelected).slot;
}
return Column(
children: [
if (scheduleData.servicesListInAppointment!.isNotEmpty) ...[
Column(
children: List.generate(scheduleData.servicesListInAppointment!.length, (serviceIndex) {
ServiceModel serviceData = scheduleData.servicesListInAppointment![serviceIndex];
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${serviceData.providerServiceDescription}".toText(fontSize: 16, isBold: true),
],
),
Column(
children: List.generate(serviceData.serviceItems!.length, (itemIndex) {
ItemData itemData = serviceData.serviceItems![itemIndex];
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
"${itemData.name}: ${itemData.price} SAR".toText(fontSize: 13, isBold: true, color: MyColors.lightTextColor),
],
),
],
);
}),
),
],
);
}),
).paddingOnly(bottom: 10),
],
5.height,
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),
"${scheduleData.selectedCustomTimeDateSlotModel!.date!.date} at ${selectedTimeSlot}".toText(fontSize: 12, isBold: true),
]),
Row(children: [
"Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
(scheduleData.appointmentType == 2 ? "Home" : "Workshop").toText(fontSize: 12, isBold: true),
]),
],
),
),
10.height,
Divider(thickness: 0.7),
Builder(builder: (BuildContext context) {
double totalServicePrice = 0.0;
double totalKms = 15.3;
double rangePricePerKm = 5;
totalServicePrice = rangePricePerKm * totalKms;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Amount".toText(fontSize: 16, isBold: true),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Service Charges".toText(fontSize: 14, color: MyColors.lightTextColor, isBold: true),
"${scheduleData.amountTotal.toString()} SAR".toText(fontSize: 16, isBold: true),
],
),
if (scheduleData.appointmentType == 1) ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Location Charges ($rangePricePerKm x $totalKms )".toText(fontSize: 14, color: MyColors.lightTextColor, isBold: true),
"${totalServicePrice.toString()} SAR".toText(fontSize: 16, isBold: true),
],
),
],
10.height,
],
);
}),
],
);
},
separatorBuilder: (BuildContext context, int index) => Divider(thickness: 2),
).paddingOnly(bottom: 10),
],
),
],
).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 0));
},
);
}
Widget buildNextButtonFooter({required BuildContext context}) {
AppointmentsVM appointmentsVM = context.read<AppointmentsVM>();
return Container(
color: MyColors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Divider(thickness: 0.7, height: 3),
8.height,
if (appointmentsVM.amountToPayForAppointment > 0) ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Payable now".toText(fontSize: 14, isBold: true),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
appointmentsVM.amountToPayForAppointment.toString().toText(fontSize: 16, isBold: true),
2.width,
"SAR".toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 2),
],
)
],
).paddingOnly(left: 21, right: 21),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Remaining Amount".toText(fontSize: 14, isBold: true),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
(appointmentsVM.totalAmount - appointmentsVM.amountToPayForAppointment).toString().toText(fontSize: 16, isBold: true),
2.width,
"SAR".toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 2),
],
)
],
).paddingOnly(left: 21, right: 21),
],
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Total Amount ".toText(fontSize: 18, isBold: true),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
appointmentsVM.totalAmount.toString().toText(fontSize: 29, isBold: true),
2.width,
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
],
)
],
).paddingOnly(left: 21, right: 21),
5.height,
SizedBox(
width: double.infinity,
child: ShowFillButton(
maxHeight: 55,
backgroundColor: MyColors.darkPrimaryColor,
title: "Book Appointment",
onPressed: () {
appointmentsVM.onBookAppointmentPressed(context);
},
).paddingOnly(bottom: 12, left: 21, right: 21),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: "Review Appointment",
isRemoveBackButton: false,
isDrawerEnabled: false,
onBackButtonTapped: () => Navigator.pop(context),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView(
children: [
buildBranchInfoCard(context: context),
buildServicesInfoCard(context: context),
],
).expand(),
buildNextButtonFooter(context: context),
],
),
);
}
}

@ -1,195 +0,0 @@
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<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) {
return DropdownField(
(DropValue value) => appointmentsVM.updateProviderCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
list: appointmentsVM.branchCategories,
hint: "Select Category",
dropdownValue: appointmentsVM.branchSelectedCategoryId.selectedId != -1
? DropValue(appointmentsVM.branchSelectedCategoryId.selectedId, appointmentsVM.branchSelectedCategoryId.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<DropValue> serviceCategories = [];
for (var element in appointmentsVM.branchServices) {
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 Service",
dropdownValue: appointmentsVM.branchSelectedServiceId.selectedId != -1
? DropValue(appointmentsVM.branchSelectedServiceId.selectedId, appointmentsVM.branchSelectedServiceId.selectedOption, "")
: null,
);
},
),
],
if (appointmentsVM.branchSelectedServiceId.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.darkPrimaryColor,
title: "Next",
onPressed: () {
bool isValidated = appointmentsVM.isServiceSelectionValidated();
if (isValidated) {
appointmentsVM.getServiceItems(appointmentsVM.branchSelectedServiceId.selectedId);
Navigator.pop(context);
navigateWithName(context, AppRoutes.bookAppointmentsItemView);
}
},
),
),
],
),
).paddingOnly(bottom: 20)
],
)).horPaddingMain();
}
}

@ -1,239 +0,0 @@
import 'dart:developer';
import 'package:car_customer_app/view_models/appointments_view_model.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/service_schedule_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:provider/provider.dart';
import 'package:intl/intl.dart';
class CustomCalenderWidget extends StatefulWidget {
final List<CustomTimeDateSlotModel> customTimeDateSlotList;
final int scheduleIndex;
const CustomCalenderWidget({super.key, required this.customTimeDateSlotList, required this.scheduleIndex});
@override
State<CustomCalenderWidget> createState() => _CustomCalenderWidgetState();
}
class _CustomCalenderWidgetState extends State<CustomCalenderWidget> {
List<DateTime> allDates = [];
List<DropValue> allMonths = [];
List<DateTime> datesInSelectedMonth = [];
CalendarFormat _calendarFormat = CalendarFormat.month;
late int selectedMonth;
late int selectedYear;
DateTime? _selectedDay;
DateTime _focusedDay = DateTime.now();
@override
void initState() {
super.initState();
populateDateList();
}
populateDateList() {
for (var value in widget.customTimeDateSlotList) {
DateTime dt = DateFormat('dd MMMM, yyyy').parse(value.date!.date);
DropValue dv = DropValue(dt.month, "${dt.month.getMonthNameByNumber()}, ${dt.year}", "${dt.year}");
allDates.add(dt);
if (!ifMonthAlreadyThere(dv)) {
allMonths.add(dv);
}
}
selectedMonth = allDates.first.month;
selectedYear = allDates.first.year;
final appointmentsVM = context.read<AppointmentsVM>();
if (appointmentsVM.serviceAppointmentScheduleList[widget.scheduleIndex].selectedCustomTimeDateSlotModel != null) {
DateTime alreadySelectedDate = DateFormat('dd MMMM, yyyy').parse(appointmentsVM.serviceAppointmentScheduleList[widget.scheduleIndex].selectedCustomTimeDateSlotModel!.date!.date);
_selectedDay = alreadySelectedDate;
_focusedDay = alreadySelectedDate;
selectedMonth = alreadySelectedDate.month;
selectedYear = alreadySelectedDate.year;
}
datesInSelectedMonth = allDates.where((element) => element.month == selectedMonth).toList();
if (appointmentsVM.serviceAppointmentScheduleList[widget.scheduleIndex].selectedCustomTimeDateSlotModel == null) {
_focusedDay = datesInSelectedMonth.first;
}
}
bool ifMonthAlreadyThere(DropValue monthDate) {
int index = allMonths.indexWhere((element) => element.id == monthDate.id && element.subValue == monthDate.subValue);
if (index == -1) {
return false;
}
return true;
}
bool ifDateAlreadyThere(DateTime dt) {
int index = allDates.indexWhere((element) => dt.month == element.month && dt.year == element.year);
if (index == -1) {
return false;
}
return true;
}
@override
Widget build(BuildContext context) {
return Consumer(
builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Builder(builder: (context) {
return DropdownField(
(DropValue value) {
setState(() {
selectedYear = int.parse(value.value.split(',')[1]);
selectedMonth = value.value.split(',')[0].getMonthNumberByName();
datesInSelectedMonth = allDates.where((element) => element.month == selectedMonth).toList();
_focusedDay = datesInSelectedMonth.first;
});
},
list: allMonths,
dropdownValue: DropValue(selectedMonth, "${selectedMonth.getMonthNameByNumber()}, ${selectedYear}", "${selectedYear}"),
hint: "${selectedMonth.getMonthNameByNumber()}, $selectedYear",
errorValue: "",
showAppointmentPickerVariant: true,
);
}),
),
Spacer(),
Icon(
Icons.calendar_today,
color: Colors.black,
size: 18,
).paddingOnly(right: 10)
],
).paddingOnly(left: 6, right: 0),
TableCalendar(
headerVisible: false,
firstDay: datesInSelectedMonth.first,
lastDay: datesInSelectedMonth.last,
focusedDay: _focusedDay,
calendarFormat: _calendarFormat,
weekendDays: [DateTime.friday, DateTime.saturday],
daysOfWeekHeight: 30,
availableGestures: AvailableGestures.none,
daysOfWeekStyle: DaysOfWeekStyle(
weekdayStyle: TextStyle(fontSize: 14, color: MyColors.black),
weekendStyle: TextStyle(fontSize: 14, color: MyColors.black.withOpacity(0.5)),
),
calendarBuilders: CalendarBuilders(
selectedBuilder: (BuildContext context, DateTime dateTime1, DateTime dateTime2) {
return Container(
height: 50,
width: 50,
margin: EdgeInsets.all(5),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: MyColors.darkIconColor,
),
alignment: Alignment.center,
child: Text(
dateTime2.day.toString(),
style: TextStyle(color: MyColors.white),
),
);
},
defaultBuilder: (BuildContext context, DateTime dateTime1, DateTime dateTime2) {
int index = datesInSelectedMonth.indexWhere((element) => isSameDay(dateTime1, element));
if (index == -1) {
return Container(
height: 50,
width: 50,
margin: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
shape: BoxShape.circle,
),
alignment: Alignment.center,
child: Text(
dateTime1.day.toString(),
),
);
}
return Container(
height: 50,
width: 50,
margin: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(color: Colors.orange, width: 2),
shape: BoxShape.circle,
),
alignment: Alignment.center,
child: Text(
dateTime1.day.toString(),
),
);
},
disabledBuilder: (BuildContext context, DateTime dateTime1, DateTime selectedDt) {
return Container(
height: 50,
width: 50,
margin: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
shape: BoxShape.circle,
),
alignment: Alignment.center,
child: Text(
dateTime1.day.toString(),
),
);
},
),
headerStyle: HeaderStyle(formatButtonVisible: false),
selectedDayPredicate: (day) {
// Use `selectedDayPredicate` to determine which day is currently selected.
// If this returns true, then `day` will be marked as selected.
// Using `isSameDay` is recommended to disregard
// the time-part of compared DateTime objects.
return isSameDay(_selectedDay, day);
},
onDaySelected: (selectedDay, focusedDay) {
int index = datesInSelectedMonth.indexWhere((element) => isSameDay(selectedDay, element));
if (index == -1) {
return;
}
if (!isSameDay(_selectedDay, selectedDay)) {
setState(() {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
});
int dateIndex = widget.customTimeDateSlotList.indexWhere((element) {
DateTime dateFromList = DateFormat('dd MMMM, yyyy').parse(element.date!.date);
if (isSameDay(dateFromList, selectedDay)) {
return true;
}
return false;
});
if (dateIndex != -1) {
appointmentsVM.updateSelectedAppointmentDate(scheduleIndex: widget.scheduleIndex, dateIndex: dateIndex);
}
}
},
),
],
);
},
);
}
}

@ -1,209 +0,0 @@
import 'package:car_customer_app/view_models/dashboard_view_model.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.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/appointments_models/appointment_list_model.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/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class CustomerAppointmentSliderWidget extends StatelessWidget {
final List<AppointmentListModel> myUpComingAppointments;
const CustomerAppointmentSliderWidget({Key? key, required this.myUpComingAppointments}) : super(key: key);
@override
Widget build(BuildContext context) {
if (myUpComingAppointments.isEmpty) {
return CustomAddButton(
needsBorder: true,
bgColor: MyColors.white,
onTap: () => context.read<DashboardVM>().onNavbarTapped(1),
text: "Add New Appointment",
icon: Container(
height: 24,
width: 24,
decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor),
child: const Icon(Icons.add, color: MyColors.white),
),
).padding(EdgeInsets.symmetric(vertical: 10, horizontal: 21));
return Container(
height: 86,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 24,
width: 24,
decoration: BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor),
child: Icon(
Icons.add,
color: MyColors.white,
),
),
SizedBox(width: 10),
"Add New Appointment".toText(
fontSize: 15,
isBold: true,
color: MyColors.lightTextColor,
),
],
),
).onPress(() {}).toWhiteContainer(width: double.infinity, margin: EdgeInsets.symmetric(horizontal: 21, vertical: 10));
}
return CarouselSlider.builder(
options: CarouselOptions(
height: 140,
viewportFraction: 1.0,
enlargeCenterPage: false,
enableInfiniteScroll: false,
),
itemCount: myUpComingAppointments.length,
itemBuilder: (BuildContext context, int itemIndex, int pageViewIndex) => BuildAppointmentContainerForCustomer(
isForHome: true,
appointmentListModel: myUpComingAppointments[itemIndex],
onTapped: () => navigateWithName(context, AppRoutes.appointmentDetailView, arguments: myUpComingAppointments[itemIndex]),
),
);
}
}
class BuildAppointmentContainerForCustomer extends StatelessWidget {
final bool? isForHome;
final AppointmentListModel? appointmentListModel;
final Function() onTapped;
const BuildAppointmentContainerForCustomer({Key? key, this.isForHome = false, required this.onTapped, required this.appointmentListModel}) : super(key: key);
Widget showServices(String title, String icon, {bool isMoreText = false}) {
return Row(
children: [
if (icon != "") ...[
SvgPicture.asset(icon),
8.width,
],
Flexible(
child: title.toText(
fontSize: 12,
isBold: true,
color: isMoreText ? MyColors.primaryColor : MyColors.black,
),
),
],
);
}
List<Widget> buildServicesFromAppointment({required AppointmentListModel appointmentListModel}) {
if (appointmentListModel.appointmentServicesList == null || appointmentListModel.appointmentServicesList!.isEmpty) {
return [SizedBox()];
}
if (appointmentListModel.appointmentServicesList!.length == 1) {
return [
showServices(
appointmentListModel.appointmentServicesList![0].providerServiceDescription,
MyAssets.modificationsIcon,
)
];
}
List<Widget> servicesList = List.generate(
2,
(index) => showServices(appointmentListModel.appointmentServicesList![index].providerServiceDescription, MyAssets.modificationsIcon),
);
if (appointmentListModel.appointmentServicesList!.length > 1) {
servicesList.add(
showServices(
"+ ${appointmentListModel.appointmentServicesList!.length - 1} More",
"",
isMoreText: true,
),
);
}
return servicesList;
}
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
isForHome != null && isForHome!
? Image.asset(
MyAssets.bnCar,
width: 56,
height: 56,
fit: BoxFit.fill,
).toCircle(borderRadius: 100)
: Image.asset(
MyAssets.bnCar,
width: 80,
height: 85,
fit: BoxFit.cover,
),
8.width,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
(appointmentListModel!.providerName ?? "").toText(color: MyColors.black, isBold: true, fontSize: 16),
Row(
children: [
MyAssets.miniClock.buildSvg(height: 12),
2.width,
"${appointmentListModel!.duration ?? ""} ${appointmentListModel!.appointmentDate!.toFormattedDateWithoutTime()}".toText(
color: MyColors.lightTextColor,
fontSize: 12,
),
],
),
9.height,
isForHome != null && isForHome!
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"Appointment Details".toText(
color: MyColors.primaryColor,
isUnderLine: true,
isBold: true,
fontSize: 14,
),
const Icon(Icons.arrow_forward),
],
)
: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: Column(
children: buildServicesFromAppointment(appointmentListModel: appointmentListModel!),
),
),
const Icon(
Icons.arrow_forward,
),
],
),
],
),
),
],
),
],
).onPress(onTapped).toWhiteContainer(width: double.infinity, allPading: 12),
);
}
}

@ -1,51 +0,0 @@
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,
],
),
),
],
),
);
}
}

@ -1,23 +1,27 @@
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/views/dashboard/fragments/my_requests_fragment.dart';
import 'package:mc_common_app/classes/app_state.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/view_models/appointments_view_model.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/view_models/requests_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/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';
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/models/general_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';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
import 'fragments/branches_fragment.dart';
class DashboardPage extends StatefulWidget {
@ -37,6 +41,7 @@ class _DashboardPageState extends State<DashboardPage> {
scheduleMicrotask(() {
context.read<AppointmentsVM>().populateAppointmentsFilterList();
context.read<AppointmentsVM>().populateBranchesFilterList();
context.read<RequestsVM>().populateRequestsFilterList();
context.read<AdVM>().populateAdsFilterList();
_onRefresh();
});
@ -45,6 +50,8 @@ class _DashboardPageState extends State<DashboardPage> {
Future<void> _onRefresh() async {
AdVM adVM = Provider.of<AdVM>(context, listen: false);
AppointmentsVM appointmentsVM = Provider.of<AppointmentsVM>(context, listen: false);
RequestsVM requestsVM = Provider.of<RequestsVM>(context, listen: false);
ChatVM chatVM = Provider.of<ChatVM>(context, listen: false);
if (appointmentsVM.myAppointments.isEmpty) {
await appointmentsVM.getMyAppointments();
}
@ -57,6 +64,9 @@ class _DashboardPageState extends State<DashboardPage> {
if (adVM.exploreAds.isEmpty) {
await adVM.getExploreAds();
}
if (requestsVM.myRequests.isEmpty) {
await requestsVM.getRequests(appType: AppType.customer);
}
if (adVM.vehicleTypes.isEmpty) {
await adVM.getVehicleTypes();
@ -65,6 +75,8 @@ class _DashboardPageState extends State<DashboardPage> {
if (adVM.vehicleAdsDurations.isEmpty) {
await adVM.getVehicleAdsDuration();
}
await chatVM.buildHubConnection();
adVM.updateVehicleAdDurationId(
SelectionModel(
selectedId: adVM.vehicleAdsDurations.first.id ?? 0,
@ -79,12 +91,12 @@ class _DashboardPageState extends State<DashboardPage> {
const AppointmentsFragment(),
const HomeFragment(),
AdsFragment(),
const SettingsFragment(),
MyRequestsFragment(),
];
String getPageTitle(int index) {
if (index == 0) {
return "Branches";
return "Select Branch";
}
if (index == 1) {
return "Appointments";
@ -96,28 +108,45 @@ class _DashboardPageState extends State<DashboardPage> {
return "Ads";
}
if (index == 4) {
return "";
return "My Requests";
}
return "";
}
@override
Widget build(BuildContext context) {
bool isHomePage = context.watch<DashboardVM>().selectedNavbarBarIndex == 2;
DashboardVmCustomer dashboardVM = context.read<DashboardVmCustomer>();
bool isHomePage = dashboardVM.selectedNavbarBarIndex == 2;
return Scaffold(
appBar: CustomAppBar(
backgroundColor: null,
leadingWidth: 100,
title: getPageTitle(context.watch<DashboardVM>().selectedNavbarBarIndex),
title: getPageTitle(dashboardVM.selectedNavbarBarIndex),
profileImageUrl: MyAssets.bnCar,
isRemoveBackButton: true,
isDrawerEnabled: isHomePage ? true : false,
onTap: () {
if (isHomePage) {
navigateWithName(context, AppRoutes.settingOptionsLanguages);
}
},
actions: [
(isHomePage
? MyAssets.notificationsBellIcon.buildSvg()
? Row(
children: [
"${AppState().currentAppType}".toText(color: MyColors.darkTextColor).onPress(() {
print("myId: ${AppState().getUser.data!.userInfo!.userId}");
// context.read<ChatVM>().buildHubConnection();
}),
10.width,
MyAssets.notificationsBellIcon.buildSvg().onPress(() {}),
15.width,
Icon(Icons.message, color: MyColors.darkTextColor).onPress(() {}),
],
)
: InkWell(
onTap: () async {
if (context.read<DashboardVM>().selectedNavbarBarIndex == 3) {
if (dashboardVM.selectedNavbarBarIndex == 3) {
await context.read<AdVM>().populateDataForAdFilter();
navigateWithName(context, AppRoutes.adsFilterView);
}
@ -127,9 +156,8 @@ class _DashboardPageState extends State<DashboardPage> {
.paddingOnly(right: 21)
],
),
drawer: CustomDrawer(dashboardVM: context.watch<DashboardVM>()),
bottomNavigationBar: CustomBottomNavbar(),
body: fragments[context.watch<DashboardVM>().selectedNavbarBarIndex],
body: fragments[context.watch<DashboardVmCustomer>().selectedNavbarBarIndex],
);
}
}

@ -5,7 +5,6 @@ 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';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
@ -81,7 +80,7 @@ class AdsFragment extends StatelessWidget {
16.height,
FiltersList(
filterList: adVM.exploreAdsFilterOptions,
onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnExploreAds(index: index, createdByRoleFilter: selectedFilterId.toCreatedByRoleEnum()),
onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnExploreAds(createdByRoleFilter: selectedFilterId.toCreatedByRoleEnum()),
needLeftPadding: false)
.paddingOnly(left: 21),
],
@ -89,7 +88,7 @@ class AdsFragment extends StatelessWidget {
16.height,
FiltersList(
filterList: adVM.myAdsFilterOptions,
onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnMyAds(index: index, adPostStatusEnum: selectedFilterId.toAdPostEnum()),
onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnMyAds(adPostStatusEnum: selectedFilterId.toAdPostEnum()),
needLeftPadding: false,
).paddingOnly(left: 21),
],

@ -1,13 +1,14 @@
import 'dart:developer';
import 'package:car_customer_app/view_models/appointments_view_model.dart';
import 'package:car_customer_app/views/appointments/widgets/customer_appointment_slider_widget.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/views/appointments/widgets/customer_appointment_slider_widget.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/appointments_models/appointment_list_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:provider/provider.dart';
@ -17,52 +18,72 @@ class AppointmentsFragment extends StatelessWidget {
@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.appointmentsFilterOptions,
onFilterTapped: (index, selectedFilterId) => appointmentsVM.applyFilterOnAppointmentsVM(appointmentStatusEnum: selectedFilterId.toAppointmentStatusEnum()),
),
16.height,
Expanded(
child: appointmentsVM.myFilteredAppointments.isEmpty
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
"No Appointments to show.".toText(fontSize: 16, color: MyColors.lightTextColor),
],
)
: ListView.builder(
shrinkWrap: true,
itemCount: appointmentsVM.myFilteredAppointments.length,
itemBuilder: (BuildContext context, int index) {
return BuildAppointmentContainerForCustomer(
onTapped: () {
AppointmentListModel appointmentModel = appointmentsVM.myFilteredAppointments[index];
appointmentModel.appointmentServicesList!.forEach((service) {
double totalServicePrice = 0.0;
service.serviceItems!.forEach((item) {
totalServicePrice = totalServicePrice + (double.parse("${item.price ?? 0.0}"));
});
service.currentTotalServicePrice = totalServicePrice;
});
navigateWithName(context, AppRoutes.appointmentDetailView, arguments: appointmentModel);
},
appointmentListModel: appointmentsVM.myFilteredAppointments[index],
);
},
),
),
],
);
return Scaffold(
body: 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.appointmentsFilterOptions,
onFilterTapped: (index, selectedFilterId) {
appointmentsVM.applyFilterOnAppointmentsVM(appointmentStatusEnum: selectedFilterId.toAppointmentStatusEnum());
}),
16.height,
Expanded(
child: RefreshIndicator(
onRefresh: () async {
appointmentsVM.getMyAppointments(isNeedToRebuild: true);
},
child: appointmentsVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: appointmentsVM.myFilteredAppointments.isEmpty
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
"No Appointments to show.".toText(fontSize: 16, color: MyColors.lightTextColor),
],
)
: ListView.builder(
shrinkWrap: true,
itemCount: appointmentsVM.myFilteredAppointments.length,
itemBuilder: (BuildContext context, int index) {
return BuildAppointmentContainerForCustomer(
onTapped: () {
AppointmentListModel appointmentModel = appointmentsVM.myFilteredAppointments[index];
appointmentModel.appointmentServicesList!.forEach((service) {
double totalServicePrice = 0.0;
service.serviceItems!.forEach((item) {
totalServicePrice = totalServicePrice + (double.parse("${item.price ?? 0.0}"));
});
service.currentTotalServicePrice = totalServicePrice;
});
navigateWithName(context, AppRoutes.appointmentDetailView, arguments: appointmentModel);
},
appointmentListModel: appointmentsVM.myFilteredAppointments[index],
);
},
),
),
),
],
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
context.read<DashboardVmCustomer>().onNavbarTapped(0);
},
backgroundColor: MyColors.darkPrimaryColor,
child: Icon(
Icons.add,
color: MyColors.white,
),
),
);
}

@ -1,4 +1,4 @@
import 'package:car_customer_app/view_models/appointments_view_model.dart';
import 'package:mc_common_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';
@ -38,44 +38,35 @@ class BranchesFragment extends StatelessWidget {
onRefresh: () async {
context.read<AppointmentsVM>().getAllNearBranches(isNeedToRebuild: true);
},
child: SingleChildScrollView(
child: Container(
child: Container(
width: double.infinity,
height: MediaQuery.of(context).size.height / 1.37,
child: Consumer<AppointmentsVM>(
builder: (context, model, _) {
if (model.state == ViewState.busy) {
return const Center(child: CircularProgressIndicator());
} else {
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];
child: appointmentsVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: appointmentsVM.nearbyBranches.isEmpty
? Center(child: LocaleKeys.no_branch.tr().toText(fontSize: 16, color: MyColors.lightTextColor))
: ListView.separated(
itemCount: appointmentsVM.nearbyBranches.length,
itemBuilder: (context, index) {
BranchDetailModel branchDetailModel = appointmentsVM.nearbyBranches[index];
return ProviderDetailsCard(
onCardTapped: () {
navigateWithName(context, AppRoutes.branchDetailPage, arguments: branchDetailModel);
},
providerImageUrl: MyAssets.bnCar,
title: branchDetailModel.branchName ?? "",
providerLocation: branchDetailModel.distanceKm.toString() + " KM",
providerName: branchDetailModel.serviceProviderName ?? "",
providerRatings: "4.9",
services: branchDetailModel.branchServices,
);
},
separatorBuilder: (context, index) {
return 12.height;
},
padding: const EdgeInsets.all(12),
);
}
},
),
),
),
return ProviderDetailsCard(
onCardTapped: () {
navigateWithName(context, AppRoutes.branchDetailPage, arguments: branchDetailModel);
},
providerImageUrl: MyAssets.bnCar,
title: branchDetailModel.branchName ?? "",
providerLocation: branchDetailModel.distanceKm.toString() + " KM",
providerName: branchDetailModel.serviceProviderName ?? "",
providerRatings: "4.9",
services: branchDetailModel.branchServices,
);
},
separatorBuilder: (context, index) {
return 12.height;
},
padding: const EdgeInsets.all(12),
)),
),
),
],

@ -1,6 +1,7 @@
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/views/appointments/widgets/customer_appointment_slider_widget.dart';
import 'package:car_customer_app/views/dashboard/widgets/drawer_widget.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/views/appointments/widgets/customer_appointment_slider_widget.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/utils/enums.dart';
@ -16,88 +17,91 @@ class HomeFragment extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
height: double.infinity,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 8),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
16.height,
ViewAllWidget(
title: "Upcoming Appointment".toUpperCase(),
subTitle: "View All",
onSubtitleTapped: () {
context.read<DashboardVM>().onNavbarTapped(1);
context.read<AppointmentsVM>().applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
},
).horPaddingMain(),
CustomerAppointmentSliderWidget(myUpComingAppointments: context.read<AppointmentsVM>().myUpComingAppointments),
7.height,
ViewAllWidget(
title: "My Recent Service Providers".toUpperCase(),
return Scaffold(
drawer: CustomDrawer(dashboardVM: context.watch<DashboardVmCustomer>()),
body: SizedBox(
width: double.infinity,
height: double.infinity,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 8),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
16.height,
ViewAllWidget(
title: "Upcoming Appointment".toUpperCase(),
subTitle: "View All",
onSubtitleTapped: () {
context.read<DashboardVM>().onNavbarTapped(0);
context.read<AppointmentsVM>().applyFilterOnProviders(index: 0);
}).horPaddingMain(),
Consumer(builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
return ServiceProviderWidget(nearbyBranches: context.read<AppointmentsVM>().nearbyBranches).horPaddingMain();
}),
Consumer(
builder: (BuildContext context, AdVM adVM, Widget? child) {
return Column(
children: [
if (adVM.myActiveAdsForHome.isNotEmpty) ...[
Column(
children: [
15.height,
ViewAllWidget(
title: "My Active Ads".toUpperCase(),
context.read<DashboardVmCustomer>().onNavbarTapped(1);
context.read<AppointmentsVM>().applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
},
).horPaddingMain(),
CustomerAppointmentSliderWidget(myUpComingAppointments: context.read<AppointmentsVM>().myUpComingAppointments),
7.height,
ViewAllWidget(
title: "My Recent Service Providers".toUpperCase(),
subTitle: "View All",
onSubtitleTapped: () {
context.read<DashboardVmCustomer>().onNavbarTapped(0);
context.read<AppointmentsVM>().applyFilterOnProviders(index: 0);
}).horPaddingMain(),
Consumer(builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
return ServiceProviderWidget(nearbyBranches: context.read<AppointmentsVM>().nearbyBranches).horPaddingMain();
}),
Consumer(
builder: (BuildContext context, AdVM adVM, Widget? child) {
return Column(
children: [
if (adVM.myActiveAdsForHome.isNotEmpty) ...[
Column(
children: [
15.height,
ViewAllWidget(
title: "My Active Ads".toUpperCase(),
subTitle: "View All",
onSubtitleTapped: () {
context.read<DashboardVmCustomer>().onNavbarTapped(3);
context.read<AdVM>().updateIsExploreAds(false);
}).horPaddingMain(),
BuildAdsList(
shouldShowAdStatus: true,
isAdsFragment: false,
adsList: adVM.myActiveAdsForHome,
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(false);
}).horPaddingMain(),
BuildAdsList(
shouldShowAdStatus: true,
isAdsFragment: false,
adsList: adVM.myActiveAdsForHome,
scrollPhysics: NeverScrollableScrollPhysics(),
),
],
)
context.read<DashboardVmCustomer>().onNavbarTapped(3);
context.read<AdVM>().updateIsExploreAds(true);
context.read<AdVM>().applyFilterOnExploreAds(createdByRoleFilter: CreatedByRoleEnum.allAds);
},
).horPaddingMain(),
BuildAdsList(
shouldShowAdStatus: false,
adsList: adVM.exploreAds.length >= 3 ? adVM.exploreAds.take(3).toList() : adVM.exploreAds,
isAdsFragment: false,
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, createdByRoleFilter: CreatedByRoleEnum.allAds);
},
).horPaddingMain(),
BuildAdsList(
shouldShowAdStatus: false,
adsList: adVM.exploreAds.length >= 3 ? adVM.exploreAds.take(3).toList() : adVM.exploreAds,
isAdsFragment: false,
scrollPhysics: NeverScrollableScrollPhysics(),
),
],
)
]
],
);
},
),
20.height,
],
);
},
),
20.height,
],
),
),
),
),

@ -0,0 +1,71 @@
import 'package:mc_common_app/view_models/requests_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/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/views/requests/widget/request_item.dart';
import 'package:mc_common_app/widgets/common_widgets/categories_list.dart';
import 'package:provider/provider.dart';
class MyRequestsFragment extends StatelessWidget {
const MyRequestsFragment({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: MyColors.backgroundColor,
width: double.infinity,
height: double.infinity,
child: Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) {
return Column(
children: [
16.height,
FiltersList(
filterList: requestsVM.requestsTypeFilterOptions,
onFilterTapped: (index, selectedFilterId) {
requestsVM.applyFilterOnRequestsVM(requestsTypeEnum: selectedFilterId.toRequestTypeStatusEnum());
},
),
8.height,
Expanded(
child: RefreshIndicator(
onRefresh: () async => await requestsVM.getRequests(isNeedToRebuild: true, appType: AppType.customer),
child: requestsVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: requestsVM.myFilteredRequests.isEmpty
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
"No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor),
],
)
: ListView.separated(
itemBuilder: (context, index) {
return RequestItem(request: requestsVM.myFilteredRequests[index], appType: AppType.customer);
},
separatorBuilder: (context, index) {
return 16.height;
},
itemCount: requestsVM.myFilteredRequests.length,
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8),
),
))
],
);
}),
),
floatingActionButton: FloatingActionButton(
onPressed: () => navigateWithName(context, AppRoutes.createRequestPage),
backgroundColor: MyColors.darkPrimaryColor,
child: const Icon(
Icons.add,
color: MyColors.white,
),
),
);
}
}

@ -1,10 +0,0 @@
import 'package:flutter/material.dart';
class SettingsFragment extends StatelessWidget {
const SettingsFragment({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container();
}
}

@ -1,4 +1,4 @@
import 'package:car_customer_app/view_models/dashboard_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -13,7 +13,7 @@ class CustomBottomNavbar extends StatelessWidget {
@override
Widget build(BuildContext context) {
DashboardVM dashboardVM = context.watch<DashboardVM>();
DashboardVmCustomer dashboardVM = context.watch<DashboardVmCustomer>();
return BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
@ -40,9 +40,9 @@ class CustomBottomNavbar extends StatelessWidget {
label: LocaleKeys.ads.tr(),
),
BottomNavigationBarItem(
icon: SvgPicture.asset(MyAssets.settingsIcon).paddingAll(5),
activeIcon: SvgPicture.asset(MyAssets.settingsIcon, color: MyColors.darkIconColor).paddingAll(5),
label: LocaleKeys.settings.tr(),
icon: SvgPicture.asset(MyAssets.icRequests).paddingAll(5),
activeIcon: SvgPicture.asset(MyAssets.icRequests, color: MyColors.darkIconColor).paddingAll(5),
label: LocaleKeys.requests.tr(),
),
],
currentIndex: dashboardVM.selectedNavbarBarIndex,

@ -1,4 +1,5 @@
import 'package:car_customer_app/view_models/dashboard_view_model.dart';
import 'package:mc_common_app/models/user_models/image_response.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
@ -8,7 +9,6 @@ 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/user/image_response.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';
@ -16,7 +16,7 @@ import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class CustomDrawer extends StatefulWidget {
final DashboardVM dashboardVM;
final DashboardVmCustomer dashboardVM;
const CustomDrawer({Key? key, required this.dashboardVM}) : super(key: key);

@ -1,4 +1,4 @@
import 'package:car_customer_app/view_models/appointments_view_model.dart';
import 'package:mc_common_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';

@ -1,4 +1,4 @@
import 'package:car_customer_app/view_models/appointments_view_model.dart';
import 'package:mc_common_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';

@ -1,8 +1,8 @@
import 'package:car_customer_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/models/services_models/item_model.dart';
import 'package:mc_common_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';

@ -34,7 +34,6 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
table_calendar: ^3.0.9
mc_common_app:
path: /Volumes/Data/Projects/Flutter/car_common_app

Loading…
Cancel
Save