import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/appointment_details_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/eye_measurement_details_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'dart:ui' as ui; import 'package:hmg_patient_app_new/presentation/appointments/appointment_payment_page.dart'; import 'package:lottie/lottie.dart'; class AppointmentCard extends StatefulWidget { final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; final MyAppointmentsViewModel myAppointmentsViewModel; final bool isLoading; final bool isFromHomePage; final bool isFromMedicalReport; final bool isForEyeMeasurements; final bool isForFeedback; final MedicalFileViewModel? medicalFileViewModel; final ContactUsViewModel? contactUsViewModel; final BookAppointmentsViewModel bookAppointmentsViewModel; final bool isForRate; // bool isAppointmentWithin4Hours = false; const AppointmentCard( {super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel, required this.bookAppointmentsViewModel, this.isLoading = false, this.isFromHomePage = false, this.isFromMedicalReport = false, this.isForEyeMeasurements = false, this.isForFeedback = false, this.medicalFileViewModel, this.contactUsViewModel, this.isForRate = false}); @override State createState() => _AppointmentCardState(); } class _AppointmentCardState extends State { Timer? _countdownTimer; Duration? _timeRemaining; Function(void Function())? _modalSetState; @override void initState() { super.initState(); // Start countdown timer if appointment requires payment if (widget.patientAppointmentHistoryResponseModel.nextAction == 15) { _startCountdownTimer(); } } @override void dispose() { _countdownTimer?.cancel(); super.dispose(); } void _startCountdownTimer() { final appointmentDate = DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate!); final expiryDate = appointmentDate.subtract(const Duration(hours: 4)); setState(() { _timeRemaining = expiryDate.difference(DateTime.now()); }); _countdownTimer?.cancel(); _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) { final newTimeRemaining = expiryDate.difference(DateTime.now()); if (newTimeRemaining.isNegative) { timer.cancel(); setState(() { _timeRemaining = Duration.zero; }); _modalSetState?.call(() { _timeRemaining = Duration.zero; }); } else { setState(() { _timeRemaining = newTimeRemaining; }); _modalSetState?.call(() { _timeRemaining = newTimeRemaining; }); } }); } // Helper method to build each time unit (number + label) Widget _buildTimeUnit(String value, String label) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( alignment: Alignment.center, child: value.toText32( isBold: true, color: AppColors.blackColor, isEnglishOnly: true, ), ), SizedBox(height: 4.h), Container( alignment: Alignment.center, child: label.toText12( color: AppColors.greyTextColor, ), ), ], ); } // Helper method to build time separator (:) Widget _buildTimeSeparator() { return Padding( padding: EdgeInsets.only(bottom: 16.h, left: 6.w, right: 6.w), child: ':'.toText32( isBold: true, color: AppColors.blackColor, ), ); } @override Widget build(BuildContext context) { final appState = getIt.get(); return InkWell( onTap: () => _goToDetails(context), child: Padding( padding: EdgeInsets.all(14.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ widget.isForRate ? SizedBox() : _buildHeader(context, appState), SizedBox(height: 16.h), _buildDoctorRow(context), SizedBox(height: 16.h), widget.isForRate ? SizedBox() : _buildActionArea(context, appState), ], ), ), ); } Widget _buildHeader(BuildContext context, AppState appState) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded(child: _buildChips(context, appState)), ], ); } Widget _buildChips(BuildContext context, AppState appState) { final isLiveCare = !widget.isLoading && widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!; return Wrap( alignment: WrapAlignment.start, direction: Axis.horizontal, spacing: 6.w, runSpacing: 6.h, children: [ AppCustomChipWidget( icon: widget.isLoading ? AppAssets.walkin_appointment_icon : (isLiveCare ? AppAssets.small_livecare_icon : AppAssets.walkin_appointment_icon), iconColor: widget.isLoading ? AppColors.textColor : (isLiveCare ? Colors.white : AppColors.textColor), labelText: widget.isLoading ? LocaleKeys.walkin.tr(context: context) : (isLiveCare ? LocaleKeys.livecare.tr(context: context) : LocaleKeys.walkin.tr(context: context)), backgroundColor: widget.isLoading ? AppColors.greyColor : (isLiveCare ? AppColors.successColor : AppColors.greyColor), textColor: widget.isLoading ? AppColors.textColor : (isLiveCare ? Colors.white : AppColors.textColor), ).toShimmer2(isShow: widget.isLoading), AppCustomChipWidget( labelText: widget.isLoading ? 'OutPatient' : (appState.isArabic() ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!), backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.1), textColor: AppColors.warningColorYellow, ).toShimmer2(isShow: widget.isLoading), AppCustomChipWidget( labelText: widget.isLoading ? 'Booked' : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!), backgroundColor: AppColors.successColor.withValues(alpha: 0.1), textColor: AppColors.successColor, ).toShimmer2(isShow: widget.isLoading), ], ).toShimmer2(isShow: widget.isLoading); } Widget _buildDoctorRow(BuildContext context) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.network( widget.isLoading ? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png' : widget.patientAppointmentHistoryResponseModel.doctorImageURL!, width: 63.h, height: 63.h, fit: BoxFit.cover, ).circle(100.r).toShimmer2(isShow: widget.isLoading), Transform.translate( offset: Offset(0.0, -20.h), child: Container( width: 40.w, height: 40.h, decoration: BoxDecoration( color: AppColors.whiteColor, shape: BoxShape.circle, // Makes the container circular border: Border.all( color: AppColors.scaffoldBgColor, // Color of the border width: 1.5.w, // Width of the border ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow), SizedBox(height: 2.h), (isFoldable || isTablet) ? "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor, isEnglishOnly: true) : "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true), ], ), ).circle(100).toShimmer2(isShow: widget.isLoading), ), ], ), SizedBox(width: 16.h), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ (widget.isLoading ? 'Dr' : "${widget.patientAppointmentHistoryResponseModel.doctorTitle}").toText16(isBold: true, maxlines: 1), (widget.isLoading ? 'John Doe' : " ${widget.patientAppointmentHistoryResponseModel.doctorNameObj!.truncate(20)}") .toText16(isBold: true, maxlines: 1, isEnglishOnly: !Utils.isArabicText(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "John Doe")), SizedBox(width: 12.w), (widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty) ? Image.network( widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", width: 20.h, height: 15.h, fit: BoxFit.cover, ) : SizedBox.shrink(), ], ).toShimmer2(isShow: widget.isLoading), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 3.h, runSpacing: 4.h, children: [ AppCustomChipWidget( labelText: widget.isLoading ? 'Cardiology' : (widget.patientAppointmentHistoryResponseModel.clinicName!.length > 20 ? '${widget.patientAppointmentHistoryResponseModel.clinicName!.substring(0, 20)}...' : widget.patientAppointmentHistoryResponseModel.clinicName!), ).toShimmer2(isShow: widget.isLoading), AppCustomChipWidget( labelText: widget.isLoading ? 'Olaya' : (widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital").length > 15 ? '${(widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital").substring(0, 15)}...' : widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital") .toShimmer2(isShow: widget.isLoading), Directionality( textDirection: ui.TextDirection.ltr, child: AppCustomChipWidget( labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), icon: AppAssets.appointment_calendar_icon, richText: widget.isLoading ? 'Cardiology'.toText10().toShimmer2(isShow: widget.isLoading) : "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)}" .toText10(isEnglishOnly: true, isBold: true), ).toShimmer2(isShow: widget.isLoading), ), // AppCustomChipWidget( // labelPadding: EdgeInsetsDirectional.only(start: -2.w, end: 6.w), // isIconPNG: true, // icon: getIt.get().getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, // iconSize: 18.h, // labelText: isLoading ? 'Cardiology' : "Patient: ${getIt.get().getAuthenticatedUser()!.firstName!}", // ).toShimmer2(isShow: isLoading), // if (!isFromMedicalReport) // AppCustomChipWidget( // icon: AppAssets.appointment_time_icon, // labelText: isLoading // ? 'Cardiology' // : DateUtil.formatDateToTimeLang( // DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false), // ).toShimmer2(isShow: isLoading), // AppCustomChipWidget( // icon: AppAssets.rating_icon, // iconColor: AppColors.ratingColorYellow, // labelText: isLoading ? "Rating" : "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}".needTranslation) // .toShimmer2(isShow: isLoading), ], ), ], ), ), ], ); } Widget _buildActionArea(BuildContext context, AppState appState) { if ((((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) || (widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) || !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID ?? 0))) && AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) { // if (((patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) && // DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15)) { // return Row( // children: [ // Expanded( // flex: 6, // child: CustomButton( // text: LocaleKeys.askDoctor.tr(context: context), // onPressed: () async { // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingDoctorAvailability.tr(context: context)); // await myAppointmentsViewModel.isDoctorAvailable( // projectID: patientAppointmentHistoryResponseModel.projectID, // doctorId: patientAppointmentHistoryResponseModel.doctorID, // clinicId: patientAppointmentHistoryResponseModel.clinicID, // onSuccess: (value) async { // if (value) { // await myAppointmentsViewModel.getAskDoctorRequestTypes(onSuccess: (val) { // LoaderBottomSheet.hideLoader(); // showCommonBottomSheetWithoutHeight( // context, // title: LocaleKeys.askDoctor.tr(context: context), // child: AskDoctorRequestTypeSelect( // askDoctorRequestTypeList: myAppointmentsViewModel.askDoctorRequestTypeList, // myAppointmentsViewModel: myAppointmentsViewModel, // patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, // ), // callBackFunc: () {}, // isFullScreen: false, // isCloseButtonVisible: true, // ); // }); // } else { // print("Doctor is not available"); // } // }); // }, // backgroundColor: AppColors.secondaryLightRedColor, // borderColor: AppColors.secondaryLightRedColor, // textColor: AppColors.primaryRedColor, // fontSize: (isFoldable || isTablet) ? 12.f : 14.f, // fontWeight: FontWeight.w600, // borderRadius: 12.r, // padding: EdgeInsets.symmetric(horizontal: 10.w), // // height: isTablet || isFoldable ? 46.h : 40.h, // height: 40.h, // icon: AppAssets.ask_doctor_icon, // iconColor: AppColors.primaryRedColor, // iconSize: 16.h, // ), // ), // SizedBox(width: 8.h), // Expanded( // flex: 1, // child: Container( // height: (isFoldable || isTablet) ? 50.h : 40.h, // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( // color: AppColors.textColor, // borderRadius: 10.h, // side: BorderSide( // color: AppColors.textColor, // width: 1.2, // ), // ), // child: Transform.flip( // flipX: appState.isArabic(), // child: Utils.buildSvgWithAssets( // icon: AppAssets.forward_arrow_icon, // iconColor: AppColors.whiteColor, // width: 24.w, // height: 24.h, // fit: BoxFit.contain, // ), // ), // ).toShimmer2(isShow: isLoading).onPress(() { // _goToDetails(context); // }), // ), // ], // ); // } else { return CustomButton( text: widget.isFromMedicalReport ? LocaleKeys.selectAppointment.tr(context: context) : LocaleKeys.viewDetails.tr(context: context), onPressed: () { if (widget.isFromMedicalReport) { if (widget.isForFeedback) { widget.contactUsViewModel!.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel); } else { widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel); } Navigator.pop(context, false); } else { Navigator.of(context) .push( CustomPageRoute( page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), ), ) .then((_) { widget.myAppointmentsViewModel.initAppointmentsViewModel(); widget.myAppointmentsViewModel.getPatientAppointments(true, false); }); } }, backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), // height: isTablet || isFoldable ? 46.h : 40.h, height: 40.h, icon: widget.isFromMedicalReport ? AppAssets.checkmark_icon : null, iconColor: AppColors.primaryRedColor, iconSize: 16.h, ); // } } else { if (widget.isFromMedicalReport) { if (widget.isForEyeMeasurements) { return SizedBox.shrink(); } else { return CustomButton( text: LocaleKeys.selectAppointment.tr(context: context), onPressed: () { if (widget.isForFeedback) { widget.contactUsViewModel!.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel); } else { widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel); } Navigator.pop(context, false); }, backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), // height: isTablet || isFoldable ? 46.h : 40.h, height: 40.h, icon: AppAssets.checkmark_icon, iconColor: AppColors.primaryRedColor, iconSize: 16.h, ); } } return (widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true) ? (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) && widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false) ? // Show only View Details button without arrow when rebooking not allowed _getArrivedButton(context) : Row( children: [ Expanded( flex: 6, child: (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? _getArrivedButton(context) : CustomButton( text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), onPressed: () => handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context), backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15), borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01), textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), fontSize: (isFoldable || isTablet) ? 12.f : 14.f, fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction), iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), iconSize: 15.h, )) .toShimmer2(isShow: widget.isLoading), ), SizedBox(width: 8.h), Expanded( flex: 1, child: Container( height: (isFoldable || isTablet) ? 50.h : 40.h, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.transparent, borderRadius: 10.h, side: BorderSide( color: AppColors.textColor, width: 1.2, ), ), child: Transform.flip( flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 24.w, height: 24.h, fit: BoxFit.contain, ), ), ).toShimmer2(isShow: widget.isLoading).onPress(() { _goToDetails(context); }), ), ], ) : CustomButton( text: LocaleKeys.viewDetails.tr(context: context), onPressed: () { Navigator.of(context) .push( CustomPageRoute( page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), ), ) .then((_) { widget.myAppointmentsViewModel.initAppointmentsViewModel(); widget.myAppointmentsViewModel.getPatientAppointments(true, false); }); }, backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), // height: isTablet || isFoldable ? 46.h : 40.h, height: 40.h, icon: widget.isFromMedicalReport ? AppAssets.checkmark_icon : null, iconColor: AppColors.primaryRedColor, iconSize: 16.h, ); } } Widget _getArrivedButton(BuildContext context) { if (widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false) { return CustomButton( text: LocaleKeys.viewDetails.tr(context: context), onPressed: () => _goToDetails(context), backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, ); } // Show Rebook button return CustomButton( borderSide: BorderSide( color: AppColors.textColor, width: 1.2, ), text: LocaleKeys.rebookSameDoctor.tr(context: context), onPressed: () => openDoctorScheduleCalendar(context), backgroundColor: AppColors.transparent, borderColor: AppColors.textColor, textColor: AppColors.blackColor, borderWidth: 1.h, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, icon: AppAssets.rebook_appointment_icon, iconColor: AppColors.blackColor, iconSize: 16.h, ); } void _goToDetails(BuildContext context) { if (widget.isFromMedicalReport) return; if (widget.isForEyeMeasurements) { Navigator.of(context).push( CustomPageRoute( page: EyeMeasurementDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), ), ); } else { if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) { widget.bookAppointmentsViewModel.getAppointmentNearestGate(projectID: widget.patientAppointmentHistoryResponseModel.projectID, clinicID: widget.patientAppointmentHistoryResponseModel.clinicID); } Navigator.of(context) .push( CustomPageRoute( page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), ), ) .then((_) { // myAppointmentsViewModel.initAppointmentsViewModel(); // myAppointmentsViewModel.getPatientAppointments(true, false); }); } } void openDoctorScheduleCalendar(BuildContext context) async { final doctor = DoctorsListResponseModel( clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, projectID: widget.patientAppointmentHistoryResponseModel.projectID, doctorID: widget.patientAppointmentHistoryResponseModel.doctorID, doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL, doctorTitle: widget.patientAppointmentHistoryResponseModel.doctorTitle, name: widget.patientAppointmentHistoryResponseModel.doctorNameObj, nationalityFlagURL: '', speciality: [], clinicName: widget.patientAppointmentHistoryResponseModel.clinicName, projectName: widget.patientAppointmentHistoryResponseModel.projectName, ); widget.bookAppointmentsViewModel.setSelectedDoctor(doctor); LoaderBottomSheet.showLoader(); await widget.bookAppointmentsViewModel.getDoctorFreeSlots( isBookingForLiveCare: false, onSuccess: (respData) async { LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( context, child: AppointmentCalendar(), callBackFunc: () {}, title: LocaleKeys.pickADate.tr(context: context), isFullScreen: false, isCloseButtonVisible: true, ); }, onError: (err) { LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( context, child: Utils.getErrorWidget(loadingText: err), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, ); }, ); } Future handleAppointmentNextAction(nextAction, BuildContext context) async { switch (nextAction) { case 0: // No action needed _goToDetails(context); break; case 10: // Confirm appointment - go to details _goToDetails(context); break; case 15: // Pending payment - show waiting modal // Ensure timer is running before showing modal if (_countdownTimer == null || !_countdownTimer!.isActive) { _startCountdownTimer(); } showCommonBottomSheetWithoutHeight( context, title: LocaleKeys.notice.tr(), child: StatefulBuilder( builder: (context, setModalState) { // Store the modal setState callback _modalSetState = setModalState; return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Lottie.asset(AppAnimations.warningAnimation, repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), SizedBox(height: 12,), LocaleKeys.upcomingPaymentPending.tr(context: context).toText14( color: AppColors.textColor, isCenter: true, ), SizedBox(height: 24.h), // Countdown Timer - DD : HH : MM : SS format with labels Directionality( textDirection: ui.TextDirection.ltr, child:Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Days _buildTimeUnit( _timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00', LocaleKeys.days.tr(context: context), ), _buildTimeSeparator(), // Hours _buildTimeUnit( _timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00', LocaleKeys.hours.tr(context: context), ), _buildTimeSeparator(), // Minutes _buildTimeUnit( _timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00', LocaleKeys.minutes.tr(context: context), ), _buildTimeSeparator(), // Seconds _buildTimeUnit( _timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00', LocaleKeys.seconds.tr(context: context), ), ], )), SizedBox(height: 24.h), // Green Acknowledge button with checkmark icon CustomButton( text: LocaleKeys.acknowledged.tr(context: context), onPressed: () { _modalSetState = null; // Clear callback when closing Navigator.of(context).pop(); }, backgroundColor: AppColors.successColor, borderColor: AppColors.successColor, textColor: Colors.white, fontSize: 16.f, fontWeight: FontWeight.w600, borderRadius: 12.r, height: 50.h, icon: AppAssets.checkmark_icon, iconColor: Colors.white, ), ], ); }, ), callBackFunc: () { _modalSetState = null; // Clear callback when closing }, isFullScreen: false, isCloseButtonVisible: true, ); break; case 20: // Pay now - navigate to payment page widget.myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true); Navigator.of(context).push( CustomPageRoute( page: AppointmentPaymentPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), ), ); break; case 50: // Confirm livecare - go to details _goToDetails(context); break; case 90: // Check-in - go to details _goToDetails(context); break; default: // Default - go to details _goToDetails(context); } } }