Merge pull request 'dev_sultan' (#233) from dev_sultan into master

Reviewed-on: #233
master
khansultan1 19 hours ago
commit ef021c1fa0

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 KiB

@ -83,6 +83,8 @@
"bookSuccessConfirmMore2412": "ستكون عملية الدفع عبر الإنترنت متاحة قبل 24 ساعة <20><>ن الموعد.", "bookSuccessConfirmMore2412": "ستكون عملية الدفع عبر الإنترنت متاحة قبل 24 ساعة <20><>ن الموعد.",
"upcomingPaymentPending": "سيتم تفعيل الدفع عبر الإنترنت قبل 24 ساعة من موعد الموعد", "upcomingPaymentPending": "سيتم تفعيل الدفع عبر الإنترنت قبل 24 ساعة من موعد الموعد",
"upcomingPaymentNow": "ادفع عبر الإنترنت الآن لتجنب الانتظار الطويل", "upcomingPaymentNow": "ادفع عبر الإنترنت الآن لتجنب الانتظار الطويل",
"timeUntilAppointment": "الوقت المتبقي حتى الموعد",
"appointmentTimePassed": "انتهى وقت الموعد",
"upcomingQR": "استخدم رمز الاستجابة السريعة لتسجيل الوصول في المستشفى", "upcomingQR": "استخدم رمز الاستجابة السريعة لتسجيل الوصول في المستشفى",
"upcomingVirtual": "هذا موعد افتراضي، يرجى الالتزام بالتعليمات لتجربة سلسة.", "upcomingVirtual": "هذا موعد افتراضي، يرجى الالتزام بالتعليمات لتجربة سلسة.",
"upcomingLivecare": "هذا موعد لايف كير", "upcomingLivecare": "هذا موعد لايف كير",
@ -307,6 +309,7 @@
"vitalSignObese": "بدين", "vitalSignObese": "بدين",
"vitalSignOverweight": "زيادة الوزن", "vitalSignOverweight": "زيادة الوزن",
"vitalSignUnderweight": "نقص الوزن", "vitalSignUnderweight": "نقص الوزن",
"vitalSignSkinny": "نحيف جداً",
"myMedical": "نشط", "myMedical": "نشط",
"myMedicalSubtitle": "الأدوية", "myMedicalSubtitle": "الأدوية",
"myDoctor": "أطبائي", "myDoctor": "أطبائي",
@ -1456,6 +1459,8 @@
"setTimerOfReminder": "ضبط مؤقت التذكير", "setTimerOfReminder": "ضبط مؤقت التذكير",
"youHaveAppointmentWithDr": "لديك موعد مع د. ", "youHaveAppointmentWithDr": "لديك موعد مع د. ",
"hours": "ساعات", "hours": "ساعات",
"minutes": "دقائق",
"seconds": "ثواني",
"secs": "ثواني", "secs": "ثواني",
"noAllergiesDataFound": "لم يتم العثور على بيانات الحساسية...", "noAllergiesDataFound": "لم يتم العثور على بيانات الحساسية...",
"heartRateDescription": "معدل ضربات قلبك يشير إلى عدد المرات التي ينبض فيها قلبك في الدقيقة", "heartRateDescription": "معدل ضربات قلبك يشير إلى عدد المرات التي ينبض فيها قلبك في الدقيقة",

@ -306,6 +306,7 @@
"vitalSignObese": "Obese", "vitalSignObese": "Obese",
"vitalSignOverweight": "Overweight", "vitalSignOverweight": "Overweight",
"vitalSignUnderweight": "Underweight", "vitalSignUnderweight": "Underweight",
"vitalSignSkinny": "Underweight",
"myMedical": "Active", "myMedical": "Active",
"myMedicalSubtitle": "Medications", "myMedicalSubtitle": "Medications",
"myDoctor": "My Doctors", "myDoctor": "My Doctors",
@ -1448,6 +1449,8 @@
"setTimerOfReminder": "Set the timer of reminder", "setTimerOfReminder": "Set the timer of reminder",
"youHaveAppointmentWithDr": "You have appointment with Dr. ", "youHaveAppointmentWithDr": "You have appointment with Dr. ",
"hours": "Hours", "hours": "Hours",
"minutes": "Minutes",
"seconds": "Seconds",
"secs": "Secs", "secs": "Secs",
"noAllergiesDataFound": "No allergies data found...", "noAllergiesDataFound": "No allergies data found...",
"heartRateDescription": "Your heart rate indicates how many times your heart beats per minute", "heartRateDescription": "Your heart rate indicates how many times your heart beats per minute",

@ -4,7 +4,7 @@ import 'package:hmg_patient_app_new/core/enums.dart';
class ApiConsts { class ApiConsts {
static const maxSmallScreen = 660; static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.preProd;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT

@ -400,6 +400,7 @@ class AppAssets {
static const String fullBodyFrontFemale = '$pngBasePath/full_body_front_female.png'; static const String fullBodyFrontFemale = '$pngBasePath/full_body_front_female.png';
static const String fullBodyBackFemale = '$pngBasePath/full_body_back_female.png'; static const String fullBodyBackFemale = '$pngBasePath/full_body_back_female.png';
static const String bmiFullBody = '$pngBasePath/bmi_image_1.png'; static const String bmiFullBody = '$pngBasePath/bmi_image_1.png';
static const String bmiFullBodyFemale = '$pngBasePath/female_bmi.png';
static const String defaultMEP = '$pngBasePath/default_MEP.png'; static const String defaultMEP = '$pngBasePath/default_MEP.png';
} }

@ -183,7 +183,7 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
@override @override
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList() async { Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList() async {
Map<String, dynamic> request = {'IsOnlineCheckIn': true, 'IsAdvancePayment': false, 'IsForER': true}; Map<String, dynamic> request = {"IsEROnlineCheckIn":true,'IsOnlineCheckIn': false, 'IsAdvancePayment': false, 'IsForER': true};
try { try {
GenericApiModel<List<HospitalsModel>>? apiResponse; GenericApiModel<List<HospitalsModel>>? apiResponse;

@ -11,6 +11,7 @@ enum VitalSignStatus {
obese, obese,
overweight, overweight,
underweight, underweight,
skinny,
} }
/// UI-only helper model for Vital Sign cards. /// UI-only helper model for Vital Sign cards.
@ -34,8 +35,9 @@ class VitalSignUiModel {
/// ///
/// Rules: /// Rules:
/// - Height is always blue. /// - Height is always blue.
/// - High, Obese, Overweight => red scheme. /// - High, Obese => red scheme.
/// - Low, Underweight => yellow scheme. /// - Overweight => orange scheme.
/// - Low, Underweight, Skinny => yellow scheme.
/// - Otherwise => green scheme (Normal). /// - Otherwise => green scheme (Normal).
static VitalSignUiModel scheme({required VitalSignStatus? status, required String label}) { static VitalSignUiModel scheme({required VitalSignStatus? status, required String label}) {
final l = label.toLowerCase(); final l = label.toLowerCase();
@ -50,10 +52,8 @@ class VitalSignUiModel {
); );
} }
// High, Obese, Overweight => red scheme (health concerns) // High, Obese => red scheme (health concerns)
if (status == VitalSignStatus.high || if (status == VitalSignStatus.high || status == VitalSignStatus.obese) {
status == VitalSignStatus.obese ||
status == VitalSignStatus.overweight) {
return VitalSignUiModel( return VitalSignUiModel(
iconBg: AppColors.chipSecondaryLightRedColor, iconBg: AppColors.chipSecondaryLightRedColor,
iconFg: AppColors.primaryRedColor, iconFg: AppColors.primaryRedColor,
@ -62,8 +62,20 @@ class VitalSignUiModel {
); );
} }
// Low, Underweight => yellow scheme (warning) // Overweight => orange scheme (warning)
if (status == VitalSignStatus.low || status == VitalSignStatus.underweight) { if (status == VitalSignStatus.overweight) {
return VitalSignUiModel(
iconBg: AppColors.warningColorYellow.withValues(alpha: 0.12),
iconFg: AppColors.warningColorYellow,
chipBg: AppColors.warningColorYellow.withValues(alpha: 0.12),
chipFg: AppColors.warningColorYellow,
);
}
// Low, Underweight, Skinny => yellow scheme (warning)
if (status == VitalSignStatus.low ||
status == VitalSignStatus.underweight ||
status == VitalSignStatus.skinny) {
final Color yellowBg = AppColors.highAndLow.withValues(alpha: 0.12); final Color yellowBg = AppColors.highAndLow.withValues(alpha: 0.12);
return VitalSignUiModel( return VitalSignUiModel(
iconBg: yellowBg, iconBg: yellowBg,
@ -98,6 +110,8 @@ class VitalSignUiModel {
return LocaleKeys.vitalSignOverweight.tr(); return LocaleKeys.vitalSignOverweight.tr();
case VitalSignStatus.underweight: case VitalSignStatus.underweight:
return LocaleKeys.vitalSignUnderweight.tr(); return LocaleKeys.vitalSignUnderweight.tr();
case VitalSignStatus.skinny:
return LocaleKeys.vitalSignSkinny.tr();
} }
} }
@ -131,22 +145,30 @@ class VitalSignUiModel {
return null; return null;
} }
// BMI >= 25 (Overweight or Obese) => High // BMI >= 30 (Obese) => Red
if (bmiResult >= 25) { if (bmiResult >= 30) {
return VitalSignStatus.high; return VitalSignStatus.obese;
}
// BMI >= 25 (Overweight) => Orange
else if (bmiResult >= 25) {
return VitalSignStatus.overweight;
} }
// BMI >= 18.5 and < 25 => Normal // BMI >= 18.5 (Normal) => Green
else if (bmiResult >= 18.5) { else if (bmiResult >= 18.5) {
return VitalSignStatus.normal; return VitalSignStatus.normal;
} }
// BMI < 18.5 (Underweight) => Low // BMI >= 16 (Underweight) => Yellow
else if (bmiResult >= 16) {
return VitalSignStatus.underweight;
}
// BMI < 16 (Skinny/Severely underweight) => Yellow
else { else {
return VitalSignStatus.low; return VitalSignStatus.skinny;
} }
} }
/// Weight status based on BMI with detailed classification /// Weight status based on BMI with detailed classification
/// Returns: Obese, Overweight, Normal, Underweight /// Returns: Obese, Overweight, Normal, Underweight, Skinny
static VitalSignStatus? weightStatus(dynamic bmi) { static VitalSignStatus? weightStatus(dynamic bmi) {
// Return null if BMI is not available or is 0 // Return null if BMI is not available or is 0
final double bmiResult = double.tryParse(bmi.toString()) ?? 0; final double bmiResult = double.tryParse(bmi.toString()) ?? 0;
@ -155,15 +177,26 @@ class VitalSignUiModel {
return null; return null;
} }
// BMI >= 30 (Obese) => Red
if (bmiResult >= 30) { if (bmiResult >= 30) {
return VitalSignStatus.obese; return VitalSignStatus.obese;
} else if (bmiResult >= 25) { }
// BMI >= 25 (Overweight) => Orange
else if (bmiResult >= 25) {
return VitalSignStatus.overweight; return VitalSignStatus.overweight;
} else if (bmiResult >= 18.5) { }
// BMI >= 18.5 (Normal) => Green
else if (bmiResult >= 18.5) {
return VitalSignStatus.normal; return VitalSignStatus.normal;
} else { }
// BMI >= 16 (Underweight) => Yellow
else if (bmiResult >= 16) {
return VitalSignStatus.underweight; return VitalSignStatus.underweight;
} }
// BMI < 16 (Skinny/Severely underweight) => Yellow
else {
return VitalSignStatus.skinny;
}
} }
} }

@ -191,6 +191,7 @@ class InsuranceViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
}, },
(apiResponse) { (apiResponse) {
patientInsuranceApprovalsList =[];
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {

@ -75,6 +75,7 @@ class PatientAppointmentHistoryResponseModel {
num? patientShareWithTax; num? patientShareWithTax;
num? patientTaxAmount; num? patientTaxAmount;
String? doctorNationalityFlagURL; String? doctorNationalityFlagURL;
bool? isClinicReBookingAllowed;
PatientAppointmentHistoryResponseModel({ PatientAppointmentHistoryResponseModel({
this.setupID, this.setupID,
@ -152,6 +153,7 @@ class PatientAppointmentHistoryResponseModel {
this.patientShareWithTax, this.patientShareWithTax,
this.patientTaxAmount, this.patientTaxAmount,
this.doctorNationalityFlagURL, this.doctorNationalityFlagURL,
this.isClinicReBookingAllowed,
}); });
PatientAppointmentHistoryResponseModel.fromJson(Map<String, dynamic> json) { PatientAppointmentHistoryResponseModel.fromJson(Map<String, dynamic> json) {
@ -241,6 +243,7 @@ class PatientAppointmentHistoryResponseModel {
patientShareWithTax = json['PatientShareWithTax']; patientShareWithTax = json['PatientShareWithTax'];
patientTaxAmount = json['PatientTaxAmount']; patientTaxAmount = json['PatientTaxAmount'];
doctorNationalityFlagURL = json['DoctorNationalityFlagURL']; doctorNationalityFlagURL = json['DoctorNationalityFlagURL'];
isClinicReBookingAllowed = json['IsClinicReBookingAllowed'];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -313,6 +316,7 @@ class PatientAppointmentHistoryResponseModel {
data['RemaniningHoursTocanPay'] = this.remaniningHoursTocanPay; data['RemaniningHoursTocanPay'] = this.remaniningHoursTocanPay;
data['SMSButtonVisable'] = this.sMSButtonVisable; data['SMSButtonVisable'] = this.sMSButtonVisable;
data['ServiceID'] = this.serviceID; data['ServiceID'] = this.serviceID;
data['IsClinicReBookingAllowed'] = this.isClinicReBookingAllowed;
return data; return data;
} }
} }

@ -52,6 +52,8 @@ class MyInvoicesViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
}, },
(apiResponse) { (apiResponse) {
_originalInvoicesList =[];
allInvoicesList =[];
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {

@ -322,6 +322,7 @@ class CodegenLoader extends AssetLoader{
"vitalSignObese": "بدين", "vitalSignObese": "بدين",
"vitalSignOverweight": "زيادة الوزن", "vitalSignOverweight": "زيادة الوزن",
"vitalSignUnderweight": "نقص الوزن", "vitalSignUnderweight": "نقص الوزن",
"vitalSignSkinny": "نحيف جداً",
"myMedical": "نشط", "myMedical": "نشط",
"myMedicalSubtitle": "الأدوية", "myMedicalSubtitle": "الأدوية",
"myDoctor": "أطبائي", "myDoctor": "أطبائي",
@ -1465,6 +1466,8 @@ class CodegenLoader extends AssetLoader{
"setTimerOfReminder": "ضبط مؤقت التذكير", "setTimerOfReminder": "ضبط مؤقت التذكير",
"youHaveAppointmentWithDr": "لديك موعد مع د. ", "youHaveAppointmentWithDr": "لديك موعد مع د. ",
"hours": "ساعات", "hours": "ساعات",
"minutes": "دقائق",
"seconds": "ثواني",
"secs": "ثواني", "secs": "ثواني",
"noAllergiesDataFound": "لم يتم العثور على بيانات الحساسية...", "noAllergiesDataFound": "لم يتم العثور على بيانات الحساسية...",
"heartRateDescription": "معدل ضربات قلبك يشير إلى عدد المرات التي ينبض فيها قلبك في الدقيقة", "heartRateDescription": "معدل ضربات قلبك يشير إلى عدد المرات التي ينبض فيها قلبك في الدقيقة",
@ -2034,6 +2037,7 @@ static const Map<String,dynamic> _en_US = {
"vitalSignObese": "Obese", "vitalSignObese": "Obese",
"vitalSignOverweight": "Overweight", "vitalSignOverweight": "Overweight",
"vitalSignUnderweight": "Underweight", "vitalSignUnderweight": "Underweight",
"vitalSignSkinny": "Underweight",
"myMedical": "Active", "myMedical": "Active",
"myMedicalSubtitle": "Medications", "myMedicalSubtitle": "Medications",
"myDoctor": "My Doctors", "myDoctor": "My Doctors",
@ -3174,6 +3178,8 @@ static const Map<String,dynamic> _en_US = {
"setTimerOfReminder": "Set the timer of reminder", "setTimerOfReminder": "Set the timer of reminder",
"youHaveAppointmentWithDr": "You have appointment with Dr. ", "youHaveAppointmentWithDr": "You have appointment with Dr. ",
"hours": "Hours", "hours": "Hours",
"minutes": "Minutes",
"seconds": "Seconds",
"secs": "Secs", "secs": "Secs",
"noAllergiesDataFound": "No allergies data found...", "noAllergiesDataFound": "No allergies data found...",
"heartRateDescription": "Your heart rate indicates how many times your heart beats per minute", "heartRateDescription": "Your heart rate indicates how many times your heart beats per minute",

@ -87,6 +87,8 @@ abstract class LocaleKeys {
static const bookSuccessConfirmMore2412 = 'bookSuccessConfirmMore2412'; static const bookSuccessConfirmMore2412 = 'bookSuccessConfirmMore2412';
static const upcomingPaymentPending = 'upcomingPaymentPending'; static const upcomingPaymentPending = 'upcomingPaymentPending';
static const upcomingPaymentNow = 'upcomingPaymentNow'; static const upcomingPaymentNow = 'upcomingPaymentNow';
static const timeUntilAppointment = 'timeUntilAppointment';
static const appointmentTimePassed = 'appointmentTimePassed';
static const upcomingQR = 'upcomingQR'; static const upcomingQR = 'upcomingQR';
static const upcomingVirtual = 'upcomingVirtual'; static const upcomingVirtual = 'upcomingVirtual';
static const upcomingLivecare = 'upcomingLivecare'; static const upcomingLivecare = 'upcomingLivecare';
@ -308,6 +310,7 @@ abstract class LocaleKeys {
static const vitalSignObese = 'vitalSignObese'; static const vitalSignObese = 'vitalSignObese';
static const vitalSignOverweight = 'vitalSignOverweight'; static const vitalSignOverweight = 'vitalSignOverweight';
static const vitalSignUnderweight = 'vitalSignUnderweight'; static const vitalSignUnderweight = 'vitalSignUnderweight';
static const vitalSignSkinny = 'vitalSignSkinny';
static const myMedical = 'myMedical'; static const myMedical = 'myMedical';
static const myMedicalSubtitle = 'myMedicalSubtitle'; static const myMedicalSubtitle = 'myMedicalSubtitle';
static const myDoctor = 'myDoctor'; static const myDoctor = 'myDoctor';
@ -1451,6 +1454,8 @@ abstract class LocaleKeys {
static const setTimerOfReminder = 'setTimerOfReminder'; static const setTimerOfReminder = 'setTimerOfReminder';
static const youHaveAppointmentWithDr = 'youHaveAppointmentWithDr'; static const youHaveAppointmentWithDr = 'youHaveAppointmentWithDr';
static const hours = 'hours'; static const hours = 'hours';
static const minutes = 'minutes';
static const seconds = 'seconds';
static const secs = 'secs'; static const secs = 'secs';
static const noAllergiesDataFound = 'noAllergiesDataFound'; static const noAllergiesDataFound = 'noAllergiesDataFound';
static const heartRateDescription = 'heartRateDescription'; static const heartRateDescription = 'heartRateDescription';

@ -74,6 +74,12 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
bool isAppointmentWithin4Hours = false; bool isAppointmentWithin4Hours = false;
// Countdown timer variables
Timer? _countdownTimer;
Duration? _timeRemaining;
String _countdownText = "";
Function(void Function())? _modalSetState; // Callback for modal state updates
@override @override
void initState() { void initState() {
scheduleMicrotask(() async { 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(); 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false); myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
@ -934,6 +1045,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
? (!(widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true) || ? (!(widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true) ||
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! || widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ||
widget.patientAppointmentHistoryResponseModel.isExecludeDoctor! || widget.patientAppointmentHistoryResponseModel.isExecludeDoctor! ||
widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false ||
!Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)) !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID))
? SizedBox.shrink() ? SizedBox.shrink()
: CustomButton( : CustomButton(
@ -1126,13 +1238,85 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
}); });
// LoaderBottomSheet.hideLoader(); // LoaderBottomSheet.hideLoader();
case 15: case 15:
getIt.get<DialogService>().showCommonBottomSheetWithoutH( // Ensure timer is running before showing modal
message: LocaleKeys.upcomingPaymentPending.tr(context: context), if (_countdownTimer == null || !_countdownTimer!.isActive) {
label: LocaleKeys.notice.tr(), _startCountdownTimer();
onOkPressed: () {}, }
okLabel: "confirm",
cancelLabel: LocaleKeys.acknowledged.tr(context: context), showCommonBottomSheetWithoutHeight(
isConfirmButton: true, 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: case 20:
myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true); myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true);

@ -392,7 +392,11 @@ class AppointmentCard extends StatelessWidget {
} }
} }
return (patientAppointmentHistoryResponseModel.isActiveDoctor ?? true) return (patientAppointmentHistoryResponseModel.isActiveDoctor ?? true)
? Row( ? (AppointmentType.isArrived(patientAppointmentHistoryResponseModel) &&
patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false)
? // Show only View Details button without arrow when rebooking not allowed
_getArrivedButton(context)
: Row(
children: [ children: [
Expanded( Expanded(
flex: 6, flex: 6,
@ -475,54 +479,22 @@ class AppointmentCard extends StatelessWidget {
} }
Widget _getArrivedButton(BuildContext context) { Widget _getArrivedButton(BuildContext context) {
// final isRecent = DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15; if (patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false) {
// return CustomButton(
// if (isRecent) { text: LocaleKeys.viewDetails.tr(context: context),
// return CustomButton( onPressed: () => _goToDetails(context),
// text: LocaleKeys.askDoctor.tr(context: context), backgroundColor: AppColors.secondaryLightRedColor,
// onPressed: () async { borderColor: AppColors.secondaryLightRedColor,
// LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingDoctorAvailability.tr(context: context)); textColor: AppColors.primaryRedColor,
// await myAppointmentsViewModel.isDoctorAvailable( fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
// projectID: patientAppointmentHistoryResponseModel.projectID, fontWeight: FontWeight.w600,
// doctorId: patientAppointmentHistoryResponseModel.doctorID, borderRadius: 12.r,
// clinicId: patientAppointmentHistoryResponseModel.clinicID, padding: EdgeInsets.symmetric(horizontal: 10.w),
// onSuccess: (value) async { height: 40.h,
// 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,
// );
// }
// Show Rebook button
return CustomButton( return CustomButton(
borderSide: BorderSide( borderSide: BorderSide(
color: AppColors.textColor, color: AppColors.textColor,

@ -80,7 +80,7 @@ class _TriagePageState extends State<TriagePage> {
} }
// Case 2: Should stop flag is true OR Case 3: Probability >= 70% OR Case 4: 7 or more questions answered // Case 2: Should stop flag is true OR Case 3: Probability >= 70% OR Case 4: 7 or more questions answered
if (viewModel.shouldStopTriage || highestProbability >= 70.0 || viewModel.triageQuestionCount >= 7) { if (viewModel.shouldStopTriage || highestProbability >= 70.0 || viewModel.triageQuestionCount >= 15) {
// Navigate to results/possible conditions screen // Navigate to results/possible conditions screen
context.navigateWithName(AppRoutes.possibleConditionsPage); context.navigateWithName(AppRoutes.possibleConditionsPage);
return; return;

@ -236,7 +236,9 @@ class _VitalSignPageState extends State<VitalSignPage> {
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
Image.asset( Image.asset(
AppAssets.bmiFullBody, getIt.get<AppState>().getAuthenticatedUser()?.gender == 1
? AppAssets.bmiFullBody
: AppAssets.bmiFullBodyFemale,
fit: BoxFit.cover, fit: BoxFit.cover,
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
), ),

Loading…
Cancel
Save