|
|
|
|
@ -74,6 +74,12 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
|
|
|
|
|
|
|
|
|
|
bool isAppointmentWithin4Hours = false;
|
|
|
|
|
|
|
|
|
|
// Countdown timer variables
|
|
|
|
|
Timer? _countdownTimer;
|
|
|
|
|
Duration? _timeRemaining;
|
|
|
|
|
String _countdownText = "";
|
|
|
|
|
Function(void Function())? _modalSetState; // Callback for modal state updates
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
scheduleMicrotask(() async {
|
|
|
|
|
@ -92,9 +98,114 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Initialize countdown timer for payment
|
|
|
|
|
if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 ||
|
|
|
|
|
widget.patientAppointmentHistoryResponseModel.nextAction == 20) {
|
|
|
|
|
_startCountdownTimer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
super.initState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _startCountdownTimer() {
|
|
|
|
|
final appointmentDate = DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate);
|
|
|
|
|
_updateTimeRemaining();
|
|
|
|
|
|
|
|
|
|
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|
|
|
|
if (mounted) {
|
|
|
|
|
_updateTimeRemaining();
|
|
|
|
|
} else {
|
|
|
|
|
timer.cancel();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _updateTimeRemaining() {
|
|
|
|
|
final appointmentDate = DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate);
|
|
|
|
|
final now = DateTime.now();
|
|
|
|
|
final difference = appointmentDate.difference(now);
|
|
|
|
|
|
|
|
|
|
if (difference.isNegative) {
|
|
|
|
|
if (mounted) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_timeRemaining = Duration.zero;
|
|
|
|
|
_countdownText = LocaleKeys.appointmentTimePassed.tr(context: context);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// Also update modal if callback is available
|
|
|
|
|
_modalSetState?.call(() {
|
|
|
|
|
_timeRemaining = Duration.zero;
|
|
|
|
|
_countdownText = LocaleKeys.appointmentTimePassed.tr(context: context);
|
|
|
|
|
});
|
|
|
|
|
_countdownTimer?.cancel();
|
|
|
|
|
} else {
|
|
|
|
|
if (mounted) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_timeRemaining = difference;
|
|
|
|
|
_countdownText = _formatDuration(difference);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// Also update modal if callback is available
|
|
|
|
|
_modalSetState?.call(() {
|
|
|
|
|
_timeRemaining = difference;
|
|
|
|
|
_countdownText = _formatDuration(difference);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String _formatDuration(Duration duration) {
|
|
|
|
|
final days = duration.inDays;
|
|
|
|
|
final hours = duration.inHours.remainder(24);
|
|
|
|
|
final minutes = duration.inMinutes.remainder(60);
|
|
|
|
|
final seconds = duration.inSeconds.remainder(60);
|
|
|
|
|
|
|
|
|
|
// Format: DD : HH : MM : SS
|
|
|
|
|
return '${days.toString().padLeft(2, '0')} : ${hours.toString().padLeft(2, '0')} : ${minutes.toString().padLeft(2, '0')} : ${seconds.toString().padLeft(2, '0')}';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
_countdownTimer?.cancel();
|
|
|
|
|
super.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
|
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
|
|
|
|
|
@ -934,6 +1045,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
|
|
|
|
|
? (!(widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true) ||
|
|
|
|
|
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ||
|
|
|
|
|
widget.patientAppointmentHistoryResponseModel.isExecludeDoctor! ||
|
|
|
|
|
widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false ||
|
|
|
|
|
!Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID))
|
|
|
|
|
? SizedBox.shrink()
|
|
|
|
|
: CustomButton(
|
|
|
|
|
@ -1126,13 +1238,85 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
|
|
|
|
|
});
|
|
|
|
|
// LoaderBottomSheet.hideLoader();
|
|
|
|
|
case 15:
|
|
|
|
|
getIt.get<DialogService>().showCommonBottomSheetWithoutH(
|
|
|
|
|
message: LocaleKeys.upcomingPaymentPending.tr(context: context),
|
|
|
|
|
label: LocaleKeys.notice.tr(),
|
|
|
|
|
onOkPressed: () {},
|
|
|
|
|
okLabel: "confirm",
|
|
|
|
|
cancelLabel: LocaleKeys.acknowledged.tr(context: context),
|
|
|
|
|
isConfirmButton: true,
|
|
|
|
|
// 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: [
|
|
|
|
|
// Message text
|
|
|
|
|
LocaleKeys.upcomingPaymentPending.tr(context: context).toText14(
|
|
|
|
|
color: AppColors.textColor,
|
|
|
|
|
isCenter: true,
|
|
|
|
|
),
|
|
|
|
|
SizedBox(height: 24.h),
|
|
|
|
|
// Countdown Timer - DD : HH : MM : SS format with labels
|
|
|
|
|
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,
|
|
|
|
|
);
|
|
|
|
|
case 20:
|
|
|
|
|
myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true);
|
|
|
|
|
|