Merge branch 'faiz_development' of http://34.17.52.79/Haroon6138/car_customer_app into faiz_development
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';
|
import 'package:mc_common_app/config/dependencies.dart';
|
||||||
|
|
||||||
class CustomerDependencies {
|
class CustomerDependencies {
|
||||||
static void addDependencies() {
|
static void addDependencies() {
|
||||||
AppDependencies.addDependencies();
|
AppDependencies.addDependencies();
|
||||||
injector.registerSingleton<ProviderRepo>(() => ProviderRepoImp());
|
|
||||||
injector.registerSingleton<AppointmentRepo>(() => ScheduleRepoImp());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue