import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart'; import 'package:diplomaticquarterapp/models/Appointments/DoctorListResponse.dart'; import 'package:diplomaticquarterapp/models/Appointments/FreeSlot.dart'; import 'package:diplomaticquarterapp/models/Appointments/timeSlot.dart'; import 'package:diplomaticquarterapp/services/appointment_services/GetDoctorsList.dart'; import 'package:diplomaticquarterapp/theme/colors.dart'; import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart'; import 'package:diplomaticquarterapp/uitl/app_toast.dart'; import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:diplomaticquarterapp/uitl/utils_new.dart'; import 'package:diplomaticquarterapp/widgets/buttons/custom_text_button.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:syncfusion_flutter_calendar/calendar.dart'; import 'dart:convert'; import '../../../uitl/date_uitl.dart'; class DocAvailableAppointments extends StatefulWidget { DoctorList doctor; static bool areSlotsAvailable = false; static bool areAppointmentsAvailable = false; static DateTime? selectedAppoDateTime; static String? selectedDate; static String? selectedTime; bool isLiveCareAppointment; final dynamic doctorSchedule; static int? initialSlotDuration; DocAvailableAppointments({required this.doctor, this.doctorSchedule, required this.isLiveCareAppointment}); @override _DocAvailableAppointmentsState createState() => _DocAvailableAppointmentsState(); } class _DocAvailableAppointmentsState extends State with TickerProviderStateMixin { late Map _events; late AnimationController _animationController; late CalendarController _calendarController; AppSharedPreferences sharedPref = new AppSharedPreferences(); var selectedDate = ""; dynamic selectedDateJSON; dynamic jsonFreeSlots; List docFreeSlots = []; List dayEvents = []; int selectedButtonIndex = 0; dynamic freeSlotsResponse; late ScrollController _scrollController; var language; bool isLiveCareSchedule = false; // String selectedLogSlots =''; @override void didUpdateWidget(covariant DocAvailableAppointments oldWidget) { if (oldWidget.doctorSchedule != widget.doctorSchedule) { _onDaySelected(DateUtil.convertStringToDate(widget.doctorSchedule['Date'])); _calendarController.selectedDate = DateUtil.convertStringToDate(widget.doctorSchedule['Date']); } super.didUpdateWidget(oldWidget); } @override void initState() { // TODO: implement initState super.initState(); final _selectedDay = DateTime.now(); _scrollController = new ScrollController(); _events = { _selectedDay: ['Event A0'] }; WidgetsBinding.instance.addPostFrameCallback((_) async { getCurrentLanguage(); isLiveCareSchedule = await this.sharedPref.getBool(IS_LIVECARE_APPOINTMENT) ?? false; if (isLiveCareSchedule != null && isLiveCareSchedule) getDoctorScheduledFreeSlots(context, widget.doctor); else { getDoctorFreeSlots(context, widget.doctor); } }); _calendarController = CalendarController(); _animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 50), ); _animationController.forward(); } @override void dispose() { _animationController.dispose(); _calendarController.dispose(); super.dispose(); } void _onDaySelected(DateTime day) { final DateFormat formatter = DateFormat('yyyy-MM-dd'); setState(() { this.selectedDate = DateUtil.getWeekDayMonthDayYearDateFormatted(day, language); openTimeSlotsPickerForDate(day, docFreeSlots); DocAvailableAppointments.selectedDate = formatter.format(day); _calendarController.selectedDate = day; print(_calendarController.selectedDate); }); } late ProjectViewModel projectViewModel; @override Widget build(BuildContext context) { projectViewModel = Provider.of(context); return SingleChildScrollView( child: Container( child: Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildTableCalendarWithBuilders(), Padding( padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), child: Text(selectedDate, style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600, letterSpacing: -0.64)), ), DocAvailableAppointments.areSlotsAvailable ? Container( height: 40, child: ListView.builder( controller: _scrollController, scrollDirection: Axis.horizontal, itemCount: dayEvents.length, itemBuilder: (context, index) { return Container( margin: EdgeInsets.only(right: (index == dayEvents.length - 1) ? 16 : 5.0, left: index == 0 ? 16 : 5), child: index == selectedButtonIndex ? getSelectedButton(index) : getNormalButton(index), ); }, ), ) : Center( child: Padding( padding: const EdgeInsets.only(left: 12.0, right: 12.0), child: Text(TranslationBase.of(context).noSlotsError, style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: CustomColors.grey)), )), ], ), ), ); } Widget _buildTableCalendarWithBuilders() { return Container( decoration: cardRadius(12), margin: EdgeInsets.all(16), clipBehavior: Clip.antiAlias, child: Padding( padding: const EdgeInsets.only(bottom: 12), child: Container( child: SfCalendar( controller: _calendarController, minDate: DateTime.now(), // showNavigationArrow: true, headerStyle: CalendarHeaderStyle(textAlign: TextAlign.center, textStyle: TextStyle(fontSize: 14.0, fontWeight: FontWeight.w600, letterSpacing: -0.46)), viewHeaderStyle: ViewHeaderStyle(dayTextStyle: TextStyle(fontSize: 12.0, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: CustomColors.black)), view: CalendarView.month, todayHighlightColor: Colors.transparent, todayTextStyle: TextStyle(color: Colors.black), selectionDecoration: containerColorRadiusBorderWidthCircular(Colors.transparent, 4, CustomColors.green, 2.5), cellBorderColor: Colors.white, dataSource: MeetingDataSource(_getDataSource()), monthViewSettings: const MonthViewSettings(appointmentDisplayMode: MonthAppointmentDisplayMode.indicator, showTrailingAndLeadingDates: false, appointmentDisplayCount: 1), onTap: (CalendarTapDetails details) { _calendarController.selectedDate = details.date; _onDaySelected(details.date!); }, ), ), )); } List _getDataSource() { final List meetings = []; _events.forEach((key, value) { final DateTime startTime = DateTime(key.year, key.month, key.day, 9, 0, 0); final DateTime endTime = startTime.add(const Duration(hours: 2)); meetings.add(Meeting("", startTime, endTime, CustomColors.green, false)); }); return meetings; } openTimeSlotsPickerForDate(DateTime dateStart, List freeSlots) { dayEvents.clear(); DateTime dateStartObj = new DateTime(dateStart.year, dateStart.month, dateStart.day, 0, 0, 0, 0, 0); freeSlots.forEach((v) { if (v.start == dateStartObj) dayEvents.add(v); }); setState(() { if (dayEvents.length != 0) { DocAvailableAppointments.areSlotsAvailable = true; selectedButtonIndex = 0; // selectedLogSlots = dayEvents[selectedButtonIndex].toString(); List> timeList = []; for (var i = 0; i < dayEvents.length; i++) { Map timeSlot = {"isoTime": dayEvents[i].isoTime, "start": dayEvents[i].start.toString(), "end": dayEvents[i].end.toString(), "vidaDate": dayEvents[i].vidaDate}; timeList.add(timeSlot); } AppSharedPreferences sharedPref = new AppSharedPreferences(); sharedPref.setString('selectedLogSlots', json.encode(timeList)); DocAvailableAppointments.selectedTime = dayEvents[selectedButtonIndex].isoTime; } else DocAvailableAppointments.areSlotsAvailable = false; }); } Future> _getJSONSlots() async { Map _eventsParsed; List slotsList = []; DateTime date; final DateFormat formatter = DateFormat('HH:mm'); final DateFormat dateFormatter = DateFormat('yyyy-MM-dd'); for (var i = 0; i < freeSlotsResponse.length; i++) { date = (isLiveCareSchedule != null && isLiveCareSchedule) ? DateUtil.convertStringToDate(freeSlotsResponse[i]) : DateUtil.convertStringToDateSaudiTimezone(freeSlotsResponse[i], int.parse(widget.doctor.projectID.toString())); slotsList.add(FreeSlot(date, ['slot'])); docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: freeSlotsResponse[i])); } _eventsParsed = Map.fromIterable(slotsList, key: (e) => e.slot, value: (e) => e.event); setState(() { DocAvailableAppointments.selectedDate = dateFormatter.format( (isLiveCareSchedule != null && isLiveCareSchedule) ? DateUtil.convertStringToDate(freeSlotsResponse[0]) : DateUtil.convertStringToDateSaudiTimezone( freeSlotsResponse[0], int.parse( widget.doctor.projectID.toString(), ), ), ); DocAvailableAppointments.selectedAppoDateTime = (isLiveCareSchedule != null && isLiveCareSchedule) ? DateUtil.convertStringToDate(freeSlotsResponse[0]) : DateUtil.convertStringToDateSaudiTimezone( freeSlotsResponse[0], int.parse( widget.doctor.projectID.toString(), ), ); selectedDate = DateUtil.getWeekDayMonthDayYearDateFormatted( (isLiveCareSchedule != null && isLiveCareSchedule) ? DateUtil.convertStringToDate(freeSlotsResponse[0]) : DateUtil.convertStringToDateSaudiTimezone( freeSlotsResponse[0], int.parse( widget.doctor.projectID.toString(), ), ), language); selectedDateJSON = freeSlotsResponse[0]; }); openTimeSlotsPickerForDate( (isLiveCareSchedule != null && isLiveCareSchedule) ? DateUtil.convertStringToDate(selectedDateJSON) : DateUtil.convertStringToDateSaudiTimezone( selectedDateJSON, int.parse( widget.doctor.projectID.toString(), ), ), docFreeSlots); _calendarController.selectedDate = (isLiveCareSchedule != null && isLiveCareSchedule) ? DateUtil.convertStringToDate(selectedDateJSON) : DateUtil.convertStringToDateSaudiTimezone( selectedDateJSON, int.parse( widget.doctor.projectID.toString(), ), ); _calendarController.displayDate = _calendarController.selectedDate; return _eventsParsed; } Widget getNormalButton(int index) { return CustomTextButton( backgroundColor: Colors.white, elevation: 0, side: BorderSide( color: Colors.black, //Color of the border style: BorderStyle.solid, //Style of the border width: 1.5 //width of the border ), onPressed: () { final timeslot = dayEvents[index]; DocAvailableAppointments.selectedAppoDateTime = timeslot.end; setState(() { selectedButtonIndex = index; DocAvailableAppointments.selectedTime = dayEvents[index].isoTime; print(DocAvailableAppointments.selectedTime); }); projectViewModel.analytics.appointment.book_appointment_time_selection(appointment_type: 'regular', dateTime: timeslot.end, doctor: widget.doctor); }, child: Text(dayEvents[index].isoTime!, style: TextStyle(fontSize: 12.0, color: Color(0xFF60686b))), ); } Widget getSelectedButton(int index) { return CustomTextButton( backgroundColor: CustomColors.green, elevation: 0, side: BorderSide( color: CustomColors.green, //Color of the border style: BorderStyle.solid, //Style of the border width: 1.5 //width of the border ), onPressed: () { setState(() { selectedButtonIndex = index; DocAvailableAppointments.selectedTime = dayEvents[index].isoTime; print(DocAvailableAppointments.selectedTime); }); }, child: Text(dayEvents[index].isoTime!, style: TextStyle(fontSize: 12.0, color: Colors.white)), ); } getDoctorFreeSlots(context, DoctorList docObject) { print(DocAvailableAppointments.initialSlotDuration); GifLoaderDialogUtils.showMyDialog(context); DoctorsListService service = new DoctorsListService(); service .getDoctorFreeSlots( int.parse( docObject.doctorID.toString(), ), int.parse( docObject.clinicID.toString(), ), int.parse( docObject.projectID.toString(), ), context, projectViewModel) .then((res) { GifLoaderDialogUtils.hideDialog(context); if (res['MessageStatus'] == 1) { if (res['FreeTimeSlots'].length != 0) { DocAvailableAppointments.initialSlotDuration = res['InitialSlotDuration']; DocAvailableAppointments.areAppointmentsAvailable = true; freeSlotsResponse = res['FreeTimeSlots']; _getJSONSlots().then((value) { setState(() => { _events.clear(), _events = value, if (widget.doctorSchedule != null) { _onDaySelected(DateUtil.convertStringToDate(widget.doctorSchedule['Date'])), _calendarController.selectedDate = DateUtil.convertStringToDate( widget.doctorSchedule['Date'], ) } }); }); } else { DocAvailableAppointments.areAppointmentsAvailable = false; DocAvailableAppointments.areSlotsAvailable = false; AppToast.showErrorToast(message: res['ErrorEndUserMessage']); } } else { AppToast.showErrorToast(message: res['ErrorEndUserMessage']); } }).catchError((err) { GifLoaderDialogUtils.hideDialog(context); AppToast.showErrorToast(message: err); print(err); }); } getDoctorScheduledFreeSlots(context, DoctorList docObject) { GifLoaderDialogUtils.showMyDialog(context); DoctorsListService service = new DoctorsListService(); service .getDoctorScheduledFreeSlots( int.parse(docObject.doctorID.toString()), int.parse(docObject.clinicID.toString()), int.parse(docObject.projectID.toString()), docObject.serviceID, context, ) .then((res) { GifLoaderDialogUtils.hideDialog(context); if (res['MessageStatus'] == 1) { if (res['PatientER_DoctorFreeSlots'].length != 0) { DocAvailableAppointments.areAppointmentsAvailable = true; freeSlotsResponse = res['PatientER_DoctorFreeSlots']; _getJSONSlots().then((value) => { setState(() => {_events.clear(), _events = value}) }); } else { DocAvailableAppointments.areAppointmentsAvailable = false; } } else { AppToast.showErrorToast(message: res['ErrorEndUserMessage']); } }).catchError((err) { GifLoaderDialogUtils.hideDialog(context); AppToast.showErrorToast(message: err); print(err); }); } getCurrentLanguage() async { var languageID = await sharedPref.getStringWithDefaultValue(APP_LANGUAGE, 'ar'); setState(() { this.language = languageID; }); } } class MeetingDataSource extends CalendarDataSource { MeetingDataSource(List source) { appointments = source; } @override DateTime getStartTime(int index) { return _getMeetingData(index)!.from; } @override DateTime getEndTime(int index) { return _getMeetingData(index)!.to; } @override String getSubject(int index) { return _getMeetingData(index)!.eventName; } @override Color getColor(int index) { return _getMeetingData(index)!.background; } @override bool isAllDay(int index) { return _getMeetingData(index)!.isAllDay; } Meeting? _getMeetingData(int index) { final dynamic meeting = appointments?[index]; Meeting? meetingData; if (meeting is Meeting) { meetingData = meeting; } return meetingData; } } class Meeting { Meeting(this.eventName, this.from, this.to, this.background, this.isAllDay); String eventName; DateTime from; DateTime to; Color background; bool isAllDay; }