Merge branch 'faiz_development'
commit
54fd8736c7
@ -1,12 +1,12 @@
|
|||||||
import 'package:car_customer_app/repositories/provider_repo.dart';
|
import 'package:car_customer_app/repositories/provider_repo.dart';
|
||||||
import 'package:car_customer_app/repositories/schedule_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<ProviderRepo>(() => ProviderRepoImp());
|
||||||
injector.registerSingleton<ScheduleRepo>(() => ScheduleRepoImp());
|
injector.registerSingleton<AppointmentRepo>(() => ScheduleRepoImp());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,159 @@
|
|||||||
|
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)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,239 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue