updates and fixes

pull/233/head
Sultan khan 1 day ago
parent a144f30f42
commit cfea843c4f

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": "هذا موعد لايف كير",
@ -1457,6 +1459,8 @@
"setTimerOfReminder": "ضبط مؤقت التذكير", "setTimerOfReminder": "ضبط مؤقت التذكير",
"youHaveAppointmentWithDr": "لديك موعد مع د. ", "youHaveAppointmentWithDr": "لديك موعد مع د. ",
"hours": "ساعات", "hours": "ساعات",
"minutes": "دقائق",
"seconds": "ثواني",
"secs": "ثواني", "secs": "ثواني",
"noAllergiesDataFound": "لم يتم العثور على بيانات الحساسية...", "noAllergiesDataFound": "لم يتم العثور على بيانات الحساسية...",
"heartRateDescription": "معدل ضربات قلبك يشير إلى عدد المرات التي ينبض فيها قلبك في الدقيقة", "heartRateDescription": "معدل ضربات قلبك يشير إلى عدد المرات التي ينبض فيها قلبك في الدقيقة",

@ -1449,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",

@ -198,9 +198,9 @@ class ApiClientImp implements ApiClient {
url = "https://hmgwebservices.com/Services/NHIC.svc/REST/GetPatientInfo"; url = "https://hmgwebservices.com/Services/NHIC.svc/REST/GetPatientInfo";
body['TokenID'] = "@dm!n"; body['TokenID'] = "@dm!n";
} }
//
body['TokenID'] = "@dm!n"; // body['TokenID'] = "@dm!n";
body['PatientID'] = 1018977; // body['PatientID'] = 1018977;
// body['PatientTypeID'] = 1; // body['PatientTypeID'] = 1;
// body['PatientOutSA'] = 0; // body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q"; // body['SessionID'] = "45786230487560q";

@ -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;

@ -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;
} }
} }

@ -1466,6 +1466,8 @@ class CodegenLoader extends AssetLoader{
"setTimerOfReminder": "ضبط مؤقت التذكير", "setTimerOfReminder": "ضبط مؤقت التذكير",
"youHaveAppointmentWithDr": "لديك موعد مع د. ", "youHaveAppointmentWithDr": "لديك موعد مع د. ",
"hours": "ساعات", "hours": "ساعات",
"minutes": "دقائق",
"seconds": "ثواني",
"secs": "ثواني", "secs": "ثواني",
"noAllergiesDataFound": "لم يتم العثور على بيانات الحساسية...", "noAllergiesDataFound": "لم يتم العثور على بيانات الحساسية...",
"heartRateDescription": "معدل ضربات قلبك يشير إلى عدد المرات التي ينبض فيها قلبك في الدقيقة", "heartRateDescription": "معدل ضربات قلبك يشير إلى عدد المرات التي ينبض فيها قلبك في الدقيقة",
@ -3176,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';
@ -1452,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;

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

Loading…
Cancel
Save