Merge pull request 'haroon_dev' (#206) from haroon_dev into master

Reviewed-on: https://34.17.182.140/Haroon6138/HMG_Patient_App_New/pulls/206
faiz_dev
Haroon6138 2 days ago
commit 8112f9bb0a

@ -34,7 +34,8 @@ android {
defaultConfig { defaultConfig {
applicationId = "com.cloudsolutions.HMGPatientApp" applicationId = "com.cloudsolutions.HMGPatientApp"
// minSdk = 24 // minSdk = 24
minSdk = 26 // minSdk = 26
minSdk = 29
targetSdk = 35 targetSdk = 35
compileSdk = 36 compileSdk = 36
// targetSdk = flutter.targetSdkVersion // targetSdk = flutter.targetSdkVersion

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 475 KiB

After

Width:  |  Height:  |  Size: 468 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

@ -301,7 +301,7 @@
"vitalSignsSubTitle": "التقارير", "vitalSignsSubTitle": "التقارير",
"myMedical": "نشط", "myMedical": "نشط",
"myMedicalSubtitle": "الأدوية", "myMedicalSubtitle": "الأدوية",
"myDoctor": "طبيبي", "myDoctor": "أطبائي",
"myDoctorSubtitle": "القائمة", "myDoctorSubtitle": "القائمة",
"eye": "العين", "eye": "العين",
"eyeSubtitle": "القياس", "eyeSubtitle": "القياس",
@ -1618,5 +1618,35 @@
"sortByLocation": "الترتيب حسب الموقع", "sortByLocation": "الترتيب حسب الموقع",
"timeForFirstReminder": "وقت التذكير الأول", "timeForFirstReminder": "وقت التذكير الأول",
"reminderRemovalNote": "يمكنك إزالتها من التقويم الخاص بك لاحقاً عن طريق إيقاف تشغيل التذكير", "reminderRemovalNote": "يمكنك إزالتها من التقويم الخاص بك لاحقاً عن طريق إيقاف تشغيل التذكير",
"communicationLanguage": "لغة التواصل" "communicationLanguage": "لغة التواصل",
"cmcServiceHeader": "فحص صحي شامل: تشخيص متقدم، معلومات صحية مفصلة",
"cmcServiceDescription": "احصل على معلومات تفصيلية عن صحتك من خلال خدمات التشخيص المتقدمة لدينا. افهم جسمك بشكل أفضل لمستقبل صحي.",
"eReferralServiceHeader": "نظام الإحالة الإلكترونية في مستشفى حبيب: تبسيط عملية إحالة المرضى",
"eReferralServiceDescription": "نُسهّل عملية نقل المرضى بسلاسة إلى مستشفى حبيب من خلال نظام الإحالة الإلكترونية الآمن لدينا. نضمن استمرارية الرعاية لكل مريض.",
"bloodDonationServiceHeader": "تبرع بالدم، أنقذ الأرواح. تبرعك يُحدث فرقاً.",
"bloodDonationServiceDescription": "تبرّع بالدم، وأنقذ الأرواح. تبرعك يبعث الأمل. انضم لحملة التبرع بالدم وكن شريان حياة للمحتاجين. كل قطرة تُحدث فرقًا!",
"healthTrackersServiceHeader": "تتبّع مؤشراتك الحيوية بسهولة ويسر",
"healthTrackersServiceDescription": "أدخل بياناتك لمراقبة معدل ضربات القلب وضغط الدم بشكل مستمر، بالإضافة إلى ملخصات دقيقة لأنشطتك اليومية. ابقَ على اطلاع وحسّن صحتك بسهولة.",
"waterConsumptionServiceHeader": "حافظ على رطوبتك، حافظ على صحتك. تتبع كمية الماء التي تشربها يومياً بكل سهولة.",
"waterConsumptionServiceDescription": "أروِ عطشك، وتابع صحتك. راقب كمية الماء التي تتناولها يومياً بكل سهولة باستخدام تطبيقنا سهل الاستخدام، مما يضمن لك الترطيب الأمثل والصحة الجيدة.",
"smartWatchServiceHeader": "قم بمزامنة ساعتك الذكية مع تطبيقات الصحة",
"smartWatchServiceDescription": "قم بتوصيل ساعتك الذكية بسلاسة بتطبيقنا الصحي لتتبع البيانات بسهولة والحصول على رؤى شخصية.",
"liveChatServiceHeader": "مساعدة الخبراء على مدار الساعة طوال أيام الأسبوع\n\nمساعدة",
"liveChatServiceDescription": "هل تحتاج إلى مساعدة؟ تتيح لك خدمة الدردشة المباشرة لدينا التواصل مع فريق دعم الخبراء للإجابة على أي أسئلة لديك حول الميزات أو الإعدادات أو استكشاف الأخطاء وإصلاحها.",
"emergencyServiceHeader": "تسجيل الوصول إلى قسم الطوارئ، أسرع من أي وقت مضى. اتصل بالإسعاف / فريق الاستجابة السريعة على الفور",
"emergencyServiceDescription": "هل تواجه حالة طبية طارئة؟ سيارات الإسعاف وفرق الاستجابة السريعة لدينا جاهزة على مدار الساعة. بالإضافة إلى ذلك، يمكنك تسجيل دخولك إلى قسم الطوارئ بسرعة لتلقي رعاية أسرع.",
"homeHealthCareServiceHeader": "صحتك، في أبهى صورها. رعاية فائقة الجودة، تصلك إلى عتبة دارك.",
"homeHealthCareServiceDescription": "نقدم لكم رعاية صحية عالية الجودة تصلكم إلى عتبة منزلكم. ممرضات ذوات خبرة يقدمون رعاية حانية في راحة منزلكم.",
"profileOnlyText": "الملف الشخصي",
"information": "معلومة",
"noFavouriteDoctors": "ليس لديك أي قائمة مفضلة حتى الآن",
"addDoctors": "إضافة الأطباء",
"favouriteList": "قائمة المفضلة",
"later": "لاحقاً",
"cancelAppointmentConfirmMessage": "هل أنت متأكد من رغبتك في إلغاء هذا الموعد؟",
"acknowledged": "معترف به",
"searchLabResults": "بحث نتائج المختبر",
"callForAssistance": "اتصل للحصول على المساعدة الفورية"
} }

@ -300,7 +300,7 @@
"vitalSignsSubTitle": "Reports", "vitalSignsSubTitle": "Reports",
"myMedical": "Active", "myMedical": "Active",
"myMedicalSubtitle": "Medications", "myMedicalSubtitle": "Medications",
"myDoctor": "My Doctor", "myDoctor": "My Doctors",
"myDoctorSubtitle": "List", "myDoctorSubtitle": "List",
"eye": "Eye", "eye": "Eye",
"eyeSubtitle": "Measurement", "eyeSubtitle": "Measurement",
@ -1611,5 +1611,33 @@
"sortByLocation": "Sort by location", "sortByLocation": "Sort by location",
"sortByNearestLocation": "Sort by nearest to your location", "sortByNearestLocation": "Sort by nearest to your location",
"giveLocationPermissionForNearestList": "Please grant location permission from app settings to see the nearest locations.", "giveLocationPermissionForNearestList": "Please grant location permission from app settings to see the nearest locations.",
"communicationLanguage": "Communication Language" "communicationLanguage": "Communication Language",
"cmcServiceHeader": "Complete Health Checkup: Advanced diagnostics, Detailed Health insights",
"cmcServiceDescription": "Get detailed insights into your health with our advanced diagnostics. Understand your body better for a healthier future.",
"eReferralServiceHeader": "HMG Hospital E-Referral: Streamlined patient referrals",
"eReferralServiceDescription": "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.",
"bloodDonationServiceHeader": "Give Blood, Save Lives. Your donation makes a difference.",
"bloodDonationServiceDescription": "Donate blood, empower lives. Your contribution creates hope. Join our blood drive and be a lifeline for those in need. Every drop counts!",
"healthTrackersServiceHeader": "Track Your Vitals with Ease and effortlessly ",
"healthTrackersServiceDescription": "Input your metrics for continuous heart rate monitoring, blood pressure and precise daily activity summaries. Stay informed and optimize your well-being with ease.",
"waterConsumptionServiceHeader": "Stay Hydrated, Stay Healthy. Track your daily water intake with ease.",
"waterConsumptionServiceDescription": "Quench your thirst, track your health. Effortlessly monitor your daily water intake with our user-friendly app, ensuring optimal hydration and well-being.",
"smartWatchServiceHeader": "Sync Your Smartwatch with Health Apps",
"smartWatchServiceDescription": "Seamlessly connect your smartwatch to our health app for effortless data tracking and personalized insights.",
"liveChatServiceHeader": "24/7 Expert\nAssistance",
"liveChatServiceDescription": "Need help ? Our live chat connects you with expert support for any questions about features, settings, or troubleshooting.",
"emergencyServiceHeader": "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly",
"emergencyServiceDescription": "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.",
"homeHealthCareServiceHeader": "Your Health, Elevated. Premium care, delivered to your doorstep.",
"homeHealthCareServiceDescription": "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.",
"profileOnlyText": "Profile",
"information": "Information",
"noFavouriteDoctors": "You don't have any favourite list yet",
"addDoctors": "Add Doctors",
"favouriteList": "Favourite List",
"later": "Later",
"cancelAppointmentConfirmMessage": "Are you sure you want to cancel this appointment?",
"acknowledged": "Acknowledged",
"searchLabResults": "Search lab results",
"callForAssistance": "Call for immediate assistance"
} }

@ -200,7 +200,7 @@ class ApiClientImp implements ApiClient {
} }
// body['TokenID'] = "@dm!n"; // body['TokenID'] = "@dm!n";
// body['PatientID'] = 1231755; // 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.preProd; static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT

@ -366,6 +366,10 @@ class AppAssets {
static const String emergencyService = '$pngBasePath/emergency_services_image.png'; static const String emergencyService = '$pngBasePath/emergency_services_image.png';
static const String cmcService = '$pngBasePath/cmc_services_image.png'; static const String cmcService = '$pngBasePath/cmc_services_image.png';
static const String eReferralService = '$pngBasePath/ereferral_services_image.png'; static const String eReferralService = '$pngBasePath/ereferral_services_image.png';
static const String carParkingService = '$pngBasePath/Carparking_services_image.png';
static const String smartWatchService = '$pngBasePath/smartwatch_services_image.png';
static const String healthTrackersService = '$pngBasePath/healthtrackers_services_image.png';
static const String livechatService = '$pngBasePath/livechat_services_image.png';
static const String maleImg = '$pngBasePath/male_img.png'; static const String maleImg = '$pngBasePath/male_img.png';
static const String femaleImg = '$pngBasePath/female_img.png'; static const String femaleImg = '$pngBasePath/female_img.png';

@ -15,37 +15,37 @@ class LoadingUtils {
static bool get isLoading => _isLoadingVisible; static bool get isLoading => _isLoadingVisible;
static showFullScreenLoader({bool barrierDismissible = true, isSuccessDialog = false, String loadingText = "Loading, Please wait..."}) { // static showFullScreenLoader({bool barrierDismissible = true, isSuccessDialog = false, String loadingText = "Loading, Please wait..."}) {
if (!_isLoadingVisible) { // if (!_isLoadingVisible) {
_isLoadingVisible = true; // _isLoadingVisible = true;
final context = _navigationService.navigatorKey.currentContext; // final context = _navigationService.navigatorKey.currentContext;
log("got the context in showFullScreenLoading"); // log("got the context in showFullScreenLoading");
if (context == null) return; // if (context == null) return;
//
showDialog( // showDialog(
barrierDismissible: barrierDismissible, // barrierDismissible: barrierDismissible,
context: context, // context: context,
barrierColor: AppColors.blackColor.withOpacity(0.5), // barrierColor: AppColors.blackColor.withOpacity(0.5),
useRootNavigator: false, // useRootNavigator: false,
useSafeArea: false, // useSafeArea: false,
builder: (BuildContext context) { // builder: (BuildContext context) {
return Container( // return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( // decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, // color: AppColors.whiteColor,
borderRadius: 20.h, // borderRadius: 20.h,
hasShadow: false, // hasShadow: false,
), // ),
child: Material( // child: Material(
child: Center( // child: Center(
child: isSuccessDialog ? Utils.getSuccessWidget(loadingText: loadingText) : Utils.getLoadingWidget(loadingText: loadingText), // child: isSuccessDialog ? Utils.getSuccessWidget(loadingText: loadingText) : Utils.getLoadingWidget(loadingText: loadingText),
).paddingSymmetrical(24.w, 0), // ).paddingSymmetrical(24.w, 0),
), // ),
); // );
}).then((value) { // }).then((value) {
_isLoadingVisible = false; // _isLoadingVisible = false;
}); // });
} // }
} // }
static hideFullScreenLoader() { static hideFullScreenLoader() {
if (!_isLoadingVisible) return; if (!_isLoadingVisible) return;

@ -768,6 +768,7 @@ class Utils {
return Row( return Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
spacing: 5.w,
children: [ children: [
Image.asset(AppAssets.mada, width: 35.h, height: 35.h), Image.asset(AppAssets.mada, width: 35.h, height: 35.h),
Image.asset( Image.asset(

@ -73,13 +73,14 @@ extension EmailValidator on String {
int? maxlines, int? maxlines,
FontStyle? fontStyle, FontStyle? fontStyle,
TextOverflow? textOverflow, TextOverflow? textOverflow,
double letterSpacing = 0}) => double letterSpacing = 0, bool isEnglishOnly = false}) =>
Text( Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
maxLines: maxlines, maxLines: maxlines,
overflow: textOverflow, overflow: textOverflow,
style: TextStyle( style: TextStyle(
fontFamily: isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins',
fontSize: 9.f, fontSize: 9.f,
fontStyle: fontStyle ?? FontStyle.normal, fontStyle: fontStyle ?? FontStyle.normal,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
@ -89,7 +90,7 @@ extension EmailValidator on String {
decorationColor: color ?? AppColors.blackColor), decorationColor: color ?? AppColors.blackColor),
); );
Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0}) => Text( Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0, bool isEnglishOnly = false,}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
maxLines: (maxLine > 0) ? maxLine : null, maxLines: (maxLine > 0) ? maxLine : null,
@ -100,6 +101,7 @@ extension EmailValidator on String {
color: color ?? AppColors.blackColor, color: color ?? AppColors.blackColor,
letterSpacing: letterSpacing, letterSpacing: letterSpacing,
decoration: isUnderLine ? TextDecoration.underline : null, decoration: isUnderLine ? TextDecoration.underline : null,
fontFamily: isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins',
), ),
); );
@ -301,10 +303,11 @@ extension EmailValidator on String {
height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.f, letterSpacing: letterSpacing ?? -1, fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.normal), height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.f, letterSpacing: letterSpacing ?? -1, fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.normal),
); );
Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing}) => Text( Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing, bool isEnglishOnly = false,}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
style: TextStyle( style: TextStyle(
fontFamily: (isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),
height: height ?? 23 / 26, color: color ?? AppColors.blackColor, fontSize: 26.f, letterSpacing: letterSpacing ?? -1, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)), height: height ?? 23 / 26, color: color ?? AppColors.blackColor, fontSize: 26.f, letterSpacing: letterSpacing ?? -1, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
); );

@ -862,17 +862,17 @@ class AuthenticationViewModel extends ChangeNotifier {
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async { resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.data is String) { if (apiResponse.data is String) {
//TODO: This Section Need to Be Testing. //TODO: This Section Need to Be Testing.
LoadingUtils.hideFullScreenLoader(); LoaderBottomSheet.hideLoader();
_dialogService.showExceptionBottomSheet(message: apiResponse.data, onOkPressed: () {}, onCancelPressed: () {}); _dialogService.showExceptionBottomSheet(message: apiResponse.data, onOkPressed: () {}, onCancelPressed: () {});
//TODO: Here We Need to Show a Dialog Of Something in the case of Fail With OK and Cancel and the Display Variable WIll be result. //TODO: Here We Need to Show a Dialog Of Something in the case of Fail With OK and Cancel and the Display Variable WIll be result.
} else { } else {
LoadingUtils.hideFullScreenLoader(); LoaderBottomSheet.hideLoader();
if (apiResponse.data["MessageStatus"] == 1) { if (apiResponse.data["MessageStatus"] == 1) {
LoadingUtils.showFullScreenLoader(isSuccessDialog: true, loadingText: "Your medical file has been created successfully. \nPlease proceed to login."); LoaderBottomSheet.showLoader(loadingText: "Your medical file has been created successfully. \nPlease proceed to login.");
//TODO: Here We Need to Show a Dialog Of Something in the case of Success. //TODO: Here We Need to Show a Dialog Of Something in the case of Success.
await clearDefaultInputValues(); // This will Clear All Default Values Of User. await clearDefaultInputValues(); // This will Clear All Default Values Of User.
Future.delayed(Duration(seconds: 1), () { Future.delayed(Duration(seconds: 1), () {
LoadingUtils.hideFullScreenLoader(); LoaderBottomSheet.hideLoader();
// _navigationService.pushAndReplace(AppRoutes.loginScreen); // _navigationService.pushAndReplace(AppRoutes.loginScreen);
_navigationService.pushAndRemoveUntil(CustomPageRoute(page: LandingNavigation()), (r) => false); _navigationService.pushAndRemoveUntil(CustomPageRoute(page: LandingNavigation()), (r) => false);
_navigationService.push(CustomPageRoute(page: LoginScreen())); _navigationService.push(CustomPageRoute(page: LoginScreen()));

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/app_state.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/dependencies.dart';
import 'package:hmg_patient_app_new/core/location_util.dart'; import 'package:hmg_patient_app_new/core/location_util.dart';
@ -11,6 +12,7 @@ import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.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_repo.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/LaserCategoryType.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/LaserCategoryType.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.dart';
@ -139,6 +141,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool applyFilters = false; bool applyFilters = false;
bool isWaitingAppointmentAvailable = false; bool isWaitingAppointmentAvailable = false;
bool isPatientRescheduleAppointment = false;
bool isWaitingAppointmentSelected = false; bool isWaitingAppointmentSelected = false;
int waitingAppointmentProjectID = 0; int waitingAppointmentProjectID = 0;
@ -157,7 +160,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
LaserCategoryType(11, 'retouch'), LaserCategoryType(11, 'retouch'),
]; ];
List<LaserCategoryType> maleLaserCategory = [ List<LaserCategoryType> maleLaserCategory = [
LaserCategoryType(1, 'body'), LaserCategoryType(1, 'bodyString'),
LaserCategoryType(2, 'face'), LaserCategoryType(2, 'face'),
LaserCategoryType(11, 'retouch'), LaserCategoryType(11, 'retouch'),
]; ];
@ -289,6 +292,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
isContinueDentalPlan = false; isContinueDentalPlan = false;
isChiefComplaintsListLoading = true; isChiefComplaintsListLoading = true;
isWaitingAppointmentSelected = false; isWaitingAppointmentSelected = false;
isPatientRescheduleAppointment = false;
bodyTypes = [maleLaserCategory, femaleLaserCategory]; bodyTypes = [maleLaserCategory, femaleLaserCategory];
// getLocation(); // getLocation();
notifyListeners(); notifyListeners();
@ -427,6 +431,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setIsPatientRescheduleAppointment(bool isPatientRescheduleAppointment) {
this.isPatientRescheduleAppointment = isPatientRescheduleAppointment;
notifyListeners();
}
void onTabChanged(int index) { void onTabChanged(int index) {
calculationID = null; calculationID = null;
isGetDocForHealthCal = false; isGetDocForHealthCal = false;
@ -652,12 +661,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
// Check if doctor is favorite after getting profile // Check if doctor is favorite after getting profile
checkIsFavouriteDoctor( if(_appState.isAuthenticated) {
patientID: _appState.getAuthenticatedUser()!.patientId!, checkIsFavouriteDoctor(
projectID: doctorsProfileResponseModel.projectID ?? 0, patientID: _appState.getAuthenticatedUser()!.patientId!,
clinicID: doctorsProfileResponseModel.clinicID ?? 0, projectID: doctorsProfileResponseModel.projectID ?? 0,
doctorID: doctorsProfileResponseModel.doctorID ?? 0, clinicID: doctorsProfileResponseModel.clinicID ?? 0,
); doctorID: doctorsProfileResponseModel.doctorID ?? 0,
);
}
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);
@ -852,64 +863,114 @@ class BookAppointmentsViewModel extends ChangeNotifier {
print(failure); print(failure);
onError!(failure.message); onError!(failure.message);
}, },
(apiResponse) { (apiResponse) async {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// onError!(apiResponse); // onError!(apiResponse);
LoadingUtils.hideFullScreenLoader(); if (isPatientRescheduleAppointment) {
showCommonBottomSheetWithoutHeight( PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
navigationService.navigatorKey.currentContext!, clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
child: Utils.getWarningWidget( projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
loadingText: apiResponse.data["ErrorEndUserMessage"], endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
isShowActionButtons: true, startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
onCancelTap: () { doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
navigationService.pop(); isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
}, originalClinicID: 0,
onConfirmTap: () async { originalProjectID: 0,
navigationService.pop(); appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel( );
appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'], await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'], // LoaderBottomSheet.hideLoader();
endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'], Future.delayed(Duration(milliseconds: 50)).then((value) async {});
startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'], // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'], await insertSpecificAppointment(
isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'], onError: (err) {},
originalClinicID: 0, onSuccess: (apiResp) async {
originalProjectID: 0,
appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
);
LoaderBottomSheet.showLoader(
loadingText: LocaleKeys.reschedulingAppo.tr(context: navigationService.navigatorKey.currentContext!),
);
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Future.delayed(Duration(milliseconds: 50)).then((value) async {}); await Future.delayed(Duration(milliseconds: 50)).then((value) async {
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: LocaleKeys.bookingYourAppointment.tr()); showCommonBottomSheetWithoutHeight(
await insertSpecificAppointment( GetIt.instance<NavigationService>().navigatorKey.currentContext!,
onError: (err) {}, child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
onSuccess: (apiResp) async { callBackFunc: () {
LoadingUtils.hideFullScreenLoader(); setIsPatientRescheduleAppointment(false);
await Future.delayed(Duration(milliseconds: 50)).then((value) async { Navigator.pushAndRemoveUntil(
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); navigationService.navigatorKey.currentContext!,
await Future.delayed(Duration(milliseconds: 4000)).then((value) { CustomPageRoute(
LoadingUtils.hideFullScreenLoader(); page: LandingNavigation(),
Navigator.pushAndRemoveUntil( ),
navigationService.navigatorKey.currentContext!, (r) => false);
CustomPageRoute( },
page: LandingNavigation(), isFullScreen: false,
), isCloseButtonVisible: false,
(r) => false); isAutoDismiss: true
);
});
});
});
} else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
loadingText: apiResponse.data["ErrorEndUserMessage"],
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
},
onConfirmTap: () async {
navigationService.pop();
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
originalClinicID: 0,
originalProjectID: 0,
appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
);
LoaderBottomSheet.showLoader(
loadingText: LocaleKeys.reschedulingAppo.tr(context: navigationService.navigatorKey.currentContext!),
);
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
LoaderBottomSheet.hideLoader();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
LoaderBottomSheet.hideLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
showCommonBottomSheetWithoutHeight(
GetIt.instance<NavigationService>().navigatorKey.currentContext!,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
callBackFunc: () {
setIsPatientRescheduleAppointment(false);
Navigator.pushAndRemoveUntil(
navigationService.navigatorKey.currentContext!,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
},
isFullScreen: false,
isCloseButtonVisible: false,
isAutoDismiss: true
);
}); });
}); });
}); });
}); }),
}), callBackFunc: () {},
callBackFunc: () {}, isFullScreen: false,
isFullScreen: false, isCloseButtonVisible: true,
isCloseButtonVisible: true, );
); }
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
if (apiResponse.data == null || apiResponse.data!.isEmpty) { if (apiResponse.data == null || apiResponse.data!.isEmpty) {
onError!("No free slots available".tr()); onError!("No free slots available".tr());
@ -946,7 +1007,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// onError!(apiResponse); // onError!(apiResponse);
LoadingUtils.hideFullScreenLoader(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!, navigationService.navigatorKey.currentContext!,
@ -982,22 +1043,28 @@ class BookAppointmentsViewModel extends ChangeNotifier {
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
navigationService.pop(); navigationService.pop();
Future.delayed(Duration(milliseconds: 50)).then((value) async {}); Future.delayed(Duration(milliseconds: 50)).then((value) async {});
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: LocaleKeys.bookingYourAppointment.tr()); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
await insertSpecificAppointment( await insertSpecificAppointment(
onError: (err) {}, onError: (err) {},
onSuccess: (apiResp) async { onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader(); LoaderBottomSheet.hideLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async { await Future.delayed(Duration(milliseconds: 50)).then((value) async {
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); showCommonBottomSheetWithoutHeight(
await Future.delayed(Duration(milliseconds: 4000)).then((value) { GetIt.instance<NavigationService>().navigatorKey.currentContext!,
LoadingUtils.hideFullScreenLoader(); child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
Navigator.pushAndRemoveUntil( callBackFunc: () {
navigationService.navigatorKey.currentContext!, setIsPatientRescheduleAppointment(false);
CustomPageRoute( Navigator.pushAndRemoveUntil(
page: LandingNavigation(), navigationService.navigatorKey.currentContext!,
), CustomPageRoute(
(r) => false); page: LandingNavigation(),
}); ),
(r) => false);
},
isFullScreen: false,
isCloseButtonVisible: false,
isAutoDismiss: true
);
}); });
}); });
}); });
@ -1491,7 +1558,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
LoadingUtils.hideFullScreenLoader(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!, navigationService.navigatorKey.currentContext!,

@ -61,4 +61,14 @@ class LaserBodyPart {
data['CategoryNameN'] = this.categoryNameN; data['CategoryNameN'] = this.categoryNameN;
return data; return data;
} }
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is LaserBodyPart &&
runtimeType == other.runtimeType &&
id == other.id;
@override
int get hashCode => id.hashCode;
} }

@ -919,8 +919,6 @@ class HmgServicesRepoImp implements HmgServicesRepo {
@override @override
Future<Either<Failure, GenericApiModel<List<VitalSignResModel>>>> getPatientVitalSign() async { Future<Either<Failure, GenericApiModel<List<VitalSignResModel>>>> getPatientVitalSign() async {
Map<String, dynamic> requestBody = { Map<String, dynamic> requestBody = {
}; };
try { try {

@ -4,6 +4,7 @@ import 'dart:core';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.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/common_models/data_points.dart'; import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/date_util.dart';
@ -71,6 +72,9 @@ class LabViewModel extends ChangeNotifier {
List<TestDetails> uniqueTestsList = []; List<TestDetails> uniqueTestsList = [];
List<String> indexedCharacterForUniqueTest = []; List<String> indexedCharacterForUniqueTest = [];
List<TestDetails> filteredUniqueTestsList = [];
List<String> filteredIndexedCharacterForUniqueTest = [];
double maxY = 0.0; double maxY = 0.0;
double minY = double.infinity; double minY = double.infinity;
double maxX = double.infinity; double maxX = double.infinity;
@ -84,6 +88,9 @@ class LabViewModel extends ChangeNotifier {
LabOrderResponseByAi? labOrderResponseByAi; LabOrderResponseByAi? labOrderResponseByAi;
LabOrdersResponseByAi? labOrdersResponseByAi; LabOrdersResponseByAi? labOrdersResponseByAi;
bool isLabAIAnalysisNeedsToBeShown = true;
bool isLabResultsHistoryShowMore = false;
LabViewModel({required this.labRepo, required this.errorHandlerService, required this.navigationService}); LabViewModel({required this.labRepo, required this.errorHandlerService, required this.navigationService});
initLabProvider() { initLabProvider() {
@ -93,6 +100,8 @@ class LabViewModel extends ChangeNotifier {
labOrderTests.clear(); labOrderTests.clear();
isLabOrdersLoading = true; isLabOrdersLoading = true;
isLabResultsLoading = true; isLabResultsLoading = true;
isLabAIAnalysisNeedsToBeShown = true;
isLabResultsHistoryShowMore = false;
patientLabOrdersByClinic.clear(); patientLabOrdersByClinic.clear();
patientLabOrdersByHospital.clear(); patientLabOrdersByHospital.clear();
patientLabOrdersViewList.clear(); patientLabOrdersViewList.clear();
@ -111,6 +120,16 @@ class LabViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setIsLabAIAnalysisNeedsToBeShown(bool isLabAIAnalysisNeedsToBeShown) {
this.isLabAIAnalysisNeedsToBeShown = isLabAIAnalysisNeedsToBeShown;
notifyListeners();
}
setIsLabResultsHistoryShowMore() {
isLabResultsHistoryShowMore = !isLabResultsHistoryShowMore;
notifyListeners();
}
void setIsSortByClinic(bool value) { void setIsSortByClinic(bool value) {
isSortByClinic = value; isSortByClinic = value;
patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital; patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
@ -218,7 +237,8 @@ class LabViewModel extends ChangeNotifier {
final clinicMap = <String, List<PatientLabOrdersResponseModel>>{}; final clinicMap = <String, List<PatientLabOrdersResponseModel>>{};
final hospitalMap = <String, List<PatientLabOrdersResponseModel>>{}; final hospitalMap = <String, List<PatientLabOrdersResponseModel>>{};
if (query.isEmpty) { if (query.isEmpty) {
// filteredLabOrders = List.from(patientLabOrders); // reset filteredLabOrders = List.from(patientLabOrders); // reset
filteredUniqueTestsList = List.from(uniqueTestsList);
for (var order in patientLabOrders) { for (var order in patientLabOrders) {
final clinicKey = (order.clinicDescription ?? 'Unknown').trim(); final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
clinicMap.putIfAbsent(clinicKey, () => []).add(order); clinicMap.putIfAbsent(clinicKey, () => []).add(order);
@ -234,7 +254,10 @@ class LabViewModel extends ChangeNotifier {
final descriptions = order.testDetails?.map((d) => d.description?.toLowerCase()).toList() ?? []; final descriptions = order.testDetails?.map((d) => d.description?.toLowerCase()).toList() ?? [];
return descriptions.any((desc) => desc != null && desc.contains(query.toLowerCase())); return descriptions.any((desc) => desc != null && desc.contains(query.toLowerCase()));
}).toList(); }).toList();
// patientLabOrders = filteredLabOrders; filteredUniqueTestsList = uniqueTestsList.where((test) {
final desc = test.description?.toLowerCase() ?? '';
return desc.contains(query.toLowerCase());
}).toList();
for (var order in filteredLabOrders) { for (var order in filteredLabOrders) {
final clinicKey = (order.clinicDescription ?? 'Unknown').trim(); final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
clinicMap.putIfAbsent(clinicKey, () => []).add(order); clinicMap.putIfAbsent(clinicKey, () => []).add(order);
@ -246,6 +269,14 @@ class LabViewModel extends ChangeNotifier {
patientLabOrdersByHospital = hospitalMap.values.toList(); patientLabOrdersByHospital = hospitalMap.values.toList();
patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital; patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
} }
// Rebuild filtered indexed characters
filteredIndexedCharacterForUniqueTest = [];
for (var test in filteredUniqueTestsList) {
String label = test.description ?? "";
if (label.isEmpty) continue;
if (filteredIndexedCharacterForUniqueTest.contains(label[0].toLowerCase())) continue;
filteredIndexedCharacterForUniqueTest.add(label[0].toLowerCase());
}
notifyListeners(); notifyListeners();
} }
@ -280,6 +311,10 @@ class LabViewModel extends ChangeNotifier {
for (var element in uniqueTests) { for (var element in uniqueTests) {
labOrderTests.add(element.description ?? ""); labOrderTests.add(element.description ?? "");
} }
// Initialize filtered lists with full data
filteredUniqueTestsList = List.from(uniqueTestsList);
filteredIndexedCharacterForUniqueTest = List.from(indexedCharacterForUniqueTest);
} }
Future<void> getLabResultsByAppointmentNo( Future<void> getLabResultsByAppointmentNo(
@ -396,7 +431,7 @@ class LabViewModel extends ChangeNotifier {
double lowRefenceValue = double.infinity; double lowRefenceValue = double.infinity;
String? flagForLowReferenceRange; String? flagForLowReferenceRange;
sortedResult.take(3).toList().reversed.forEach((element) { sortedResult.toList().reversed.forEach((element) {
try { try {
var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!); var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
var resultValue = double.parse(element.resultValue!); var resultValue = double.parse(element.resultValue!);
@ -787,7 +822,13 @@ class LabViewModel extends ChangeNotifier {
Future<void> getAiOverviewLabOrders({required PatientLabOrdersResponseModel labOrder, required String loadingText}) async { Future<void> getAiOverviewLabOrders({required PatientLabOrdersResponseModel labOrder, required String loadingText}) async {
// LoadingUtils.showFullScreenLoader(loadingText: "Loading and analysing your data,\nPlease be patient and let the AI do the magic. \nPlease be patient, This might take some time."); // LoadingUtils.showFullScreenLoader(loadingText: "Loading and analysing your data,\nPlease be patient and let the AI do the magic. \nPlease be patient, This might take some time.");
LoadingUtils.showFullScreenLoader(loadingText: loadingText); // LoadingUtils.showFullScreenLoader(loadingText: loadingText);
LoaderBottomSheet.showLoader(
loadingText: loadingText,
showCloseButton: true,
onCloseTap: () {
setIsLabAIAnalysisNeedsToBeShown(false);
});
List<Map<String, dynamic>> results = []; List<Map<String, dynamic>> results = [];
Map<String, dynamic> orderData = {"order_date": labOrder.orderDate ?? "", "clinic": labOrder.clinicDescription ?? "", "doctor": labOrder.doctorName ?? "", "results": []}; Map<String, dynamic> orderData = {"order_date": labOrder.orderDate ?? "", "clinic": labOrder.clinicDescription ?? "", "doctor": labOrder.doctorName ?? "", "results": []};
List<Map<String, dynamic>> testResults = []; List<Map<String, dynamic>> testResults = [];
@ -810,16 +851,20 @@ class LabViewModel extends ChangeNotifier {
result.fold( result.fold(
(failure) async { (failure) async {
LoadingUtils.hideFullScreenLoader(); LoadingUtils.hideFullScreenLoader();
await errorHandlerService.handleError(failure: failure); if (isLabAIAnalysisNeedsToBeShown) {
await errorHandlerService.handleError(failure: failure);
}
}, },
(apiResponse) { (apiResponse) {
LoadingUtils.hideFullScreenLoader(); LoadingUtils.hideFullScreenLoader();
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
labOrdersResponseByAi = apiResponse.data; if (isLabAIAnalysisNeedsToBeShown) {
navigationService.push( labOrdersResponseByAi = apiResponse.data;
MaterialPageRoute(builder: (_) => LabAiAnalysisDetailedPage()), navigationService.push(
); MaterialPageRoute(builder: (_) => LabAiAnalysisDetailedPage()),
);
}
} }
}, },
); );
@ -828,7 +873,12 @@ class LabViewModel extends ChangeNotifier {
} }
Future<void> getAiOverviewSingleLabResult({required String langId, required LabResult recentLabResult, required String loadingText}) async { Future<void> getAiOverviewSingleLabResult({required String langId, required LabResult recentLabResult, required String loadingText}) async {
LoaderBottomSheet.showLoader(loadingText: loadingText); LoaderBottomSheet.showLoader(
loadingText: loadingText,
showCloseButton: true,
onCloseTap: () {
setIsLabAIAnalysisNeedsToBeShown(false);
});
List<Map<String, dynamic>> results = []; List<Map<String, dynamic>> results = [];
results.add({ results.add({
"Description": recentLabResult.description ?? '', "Description": recentLabResult.description ?? '',
@ -838,19 +888,24 @@ class LabViewModel extends ChangeNotifier {
"ReferanceRange": recentLabResult.referanceRange ?? '', "ReferanceRange": recentLabResult.referanceRange ?? '',
}); });
var payload = {"patient_id": currentlySelectedPatientOrder!.patientID, "language_id": langId, "lab_results": results}; // var payload = {"patient_id": currentlySelectedPatientOrder!.patientID, "language_id": langId, "lab_results": results};
var payload = {"patient_id": getIt.get<AppState>().getAuthenticatedUser()!.patientId, "language_id": langId, "lab_results": results};
final result = await labRepo.getPatientAiOverViewLabOrder(payload); final result = await labRepo.getPatientAiOverViewLabOrder(payload);
result.fold( result.fold(
(failure) async { (failure) async {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
await errorHandlerService.handleError(failure: failure); if (isLabAIAnalysisNeedsToBeShown) {
await errorHandlerService.handleError(failure: failure);
}
}, },
(apiResponse) { (apiResponse) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
labOrderResponseByAi = apiResponse.data; if (isLabAIAnalysisNeedsToBeShown) {
notifyListeners(); labOrderResponseByAi = apiResponse.data;
notifyListeners();
}
} }
}, },
); );

@ -87,6 +87,8 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
hmcCount = registeredDoctorMap.hmcSize; hmcCount = registeredDoctorMap.hmcSize;
hmgCount = registeredDoctorMap.hmgSize; hmgCount = registeredDoctorMap.hmgSize;
hospitalList!.sort((a, b) => num.parse(a.distanceInKMs!).compareTo(num.parse(b.distanceInKMs!)));
getDisplayList(); getDisplayList();
} }

@ -513,7 +513,10 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
try { try {
final list = response['PatientDoctorAppointmentResultList']; final list = response['PatientDoctorAppointmentResultList'];
final appointmentsList = list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientAppointmentHistoryResponseModel>(); List<PatientAppointmentHistoryResponseModel> appointmentsList =
list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientAppointmentHistoryResponseModel>();
// appointmentsList.removeWhere((element) => element.isActiveDoctorProfile == false);
apiResponse = GenericApiModel<List<PatientAppointmentHistoryResponseModel>>( apiResponse = GenericApiModel<List<PatientAppointmentHistoryResponseModel>>(
messageStatus: messageStatus, messageStatus: messageStatus,

@ -94,6 +94,10 @@ class MyAppointmentsViewModel extends ChangeNotifier {
selectedTabIndex = index; selectedTabIndex = index;
start = null; start = null;
end = null; end = null;
// if (index == 0) {
// filteredAppointmentList.clear();
// filteredAppointmentList.addAll(patientAppointmentsHistoryList);
// }
notifyListeners(); notifyListeners();
} }

@ -12,6 +12,10 @@ abstract class NotificationsRepo {
required int pagingSize, required int pagingSize,
required int currentPage, required int currentPage,
}); });
Future<Either<Failure, GenericApiModel<List<NotificationResponseModel>>>> markAsRead({
required int notificationID,
});
} }
class NotificationsRepoImp implements NotificationsRepo { class NotificationsRepoImp implements NotificationsRepo {
@ -75,4 +79,38 @@ class NotificationsRepoImp implements NotificationsRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel<List<NotificationResponseModel>>>> markAsRead({required int notificationID}) async {
Map<String, dynamic> mapDevice = {"NotificationPoolID": notificationID};
try {
GenericApiModel<List<NotificationResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
PUSH_NOTIFICATION_SET_MESSAGES_FROM_POOL_AS_READ,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<List<NotificationResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: [],
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
} }

@ -7,13 +7,15 @@ class NotificationsViewModel extends ChangeNotifier {
bool isNotificationsLoading = false; bool isNotificationsLoading = false;
bool hasMoreNotifications = true; bool hasMoreNotifications = true;
int unreadNotificationsCount = 0;
NotificationsRepo notificationsRepo; NotificationsRepo notificationsRepo;
ErrorHandlerService errorHandlerService; ErrorHandlerService errorHandlerService;
List<NotificationResponseModel> notificationsList = []; List<NotificationResponseModel> notificationsList = [];
int currentPage = 0; int currentPage = 0;
int pagingSize = 14; int pagingSize = 15;
int notificationStatusID = 2; // Default to status 2 (e.g., unread) int notificationStatusID = 2; // Default to status 2 (e.g., unread)
NotificationsViewModel({ NotificationsViewModel({
@ -46,6 +48,7 @@ class NotificationsViewModel extends ChangeNotifier {
Function(String)? onError, Function(String)? onError,
}) async { }) async {
isNotificationsLoading = true; isNotificationsLoading = true;
unreadNotificationsCount = 0;
notifyListeners(); notifyListeners();
final result = await notificationsRepo.getAllNotifications( final result = await notificationsRepo.getAllNotifications(
@ -78,6 +81,11 @@ class NotificationsViewModel extends ChangeNotifier {
} }
notificationsList.addAll(newNotifications); notificationsList.addAll(newNotifications);
for (var notification in notificationsList) {
if (notification.isRead == false) {
unreadNotificationsCount++;
}
}
currentPage++; currentPage++;
notifyListeners(); notifyListeners();
@ -89,6 +97,21 @@ class NotificationsViewModel extends ChangeNotifier {
); );
} }
Future<void> markAsRead({
int notificationID = 0,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await notificationsRepo.markAsRead(notificationID: notificationID);
result.fold(
(failure) async {},
(apiResponse) {
refreshNotifications();
},
);
}
Future<void> refreshNotifications({ Future<void> refreshNotifications({
Function(dynamic)? onSuccess, Function(dynamic)? onSuccess,
Function(String)? onError, Function(String)? onError,

@ -1597,6 +1597,7 @@ abstract class LocaleKeys {
static const timeFor = 'timeFor'; static const timeFor = 'timeFor';
static const hmgPolicies = 'hmgPolicies'; static const hmgPolicies = 'hmgPolicies';
static const darkMode = 'darkMode'; static const darkMode = 'darkMode';
static const featureComingSoonDescription = 'featureComingSoonDescription';
static const generateAiAnalysisResult = 'generateAiAnalysisResult'; static const generateAiAnalysisResult = 'generateAiAnalysisResult';
static const ratings = 'ratings'; static const ratings = 'ratings';
static const hmgPharmacyText = 'hmgPharmacyText'; static const hmgPharmacyText = 'hmgPharmacyText';
@ -1611,6 +1612,33 @@ abstract class LocaleKeys {
static const timeForFirstReminder = 'timeForFirstReminder'; static const timeForFirstReminder = 'timeForFirstReminder';
static const reminderRemovalNote = 'reminderRemovalNote'; static const reminderRemovalNote = 'reminderRemovalNote';
static const communicationLanguage = 'communicationLanguage'; static const communicationLanguage = 'communicationLanguage';
static const featureComingSoonDescription = 'featureComingSoonDescription'; static const cmcServiceHeader = 'cmcServiceHeader';
static const cmcServiceDescription = 'cmcServiceDescription';
static const eReferralServiceHeader = 'eReferralServiceHeader';
static const eReferralServiceDescription = 'eReferralServiceDescription';
static const bloodDonationServiceHeader = 'bloodDonationServiceHeader';
static const bloodDonationServiceDescription = 'bloodDonationServiceDescription';
static const healthTrackersServiceHeader = 'healthTrackersServiceHeader';
static const healthTrackersServiceDescription = 'healthTrackersServiceDescription';
static const waterConsumptionServiceHeader = 'waterConsumptionServiceHeader';
static const waterConsumptionServiceDescription = 'waterConsumptionServiceDescription';
static const smartWatchServiceHeader = 'smartWatchServiceHeader';
static const smartWatchServiceDescription = 'smartWatchServiceDescription';
static const liveChatServiceHeader = 'liveChatServiceHeader';
static const liveChatServiceDescription = 'liveChatServiceDescription';
static const emergencyServiceHeader = 'emergencyServiceHeader';
static const emergencyServiceDescription = 'emergencyServiceDescription';
static const homeHealthCareServiceHeader = 'homeHealthCareServiceHeader';
static const homeHealthCareServiceDescription = 'homeHealthCareServiceDescription';
static const profileOnlyText = 'profileOnlyText';
static const information = 'information';
static const noFavouriteDoctors = 'noFavouriteDoctors';
static const addDoctors = 'addDoctors';
static const favouriteList = 'favouriteList';
static const later = 'later';
static const cancelAppointmentConfirmMessage = 'cancelAppointmentConfirmMessage';
static const acknowledged = 'acknowledged';
static const searchLabResults = 'searchLabResults';
static const callForAssistance = 'callForAssistance';
} }

@ -36,7 +36,9 @@ import 'package:hmg_patient_app_new/presentation/ask_doctor/ask_doctor_page.dart
import 'package:hmg_patient_app_new/presentation/ask_doctor/doctor_response_page.dart'; import 'package:hmg_patient_app_new/presentation/ask_doctor/doctor_response_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/feedback_page.dart'; import 'package:hmg_patient_app_new/presentation/contact_us/feedback_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -73,10 +75,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
void initState() { void initState() {
scheduleMicrotask(() async { scheduleMicrotask(() async {
if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) { if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) {
CalenderUtilsNew calendarUtils = await CalenderUtilsNew.instance; // CalenderUtilsNew calendarUtils = await CalenderUtilsNew.instance;
var doesExist = await calendarUtils.checkIfEventExist("${widget.patientAppointmentHistoryResponseModel.appointmentNo}"); // var doesExist = await calendarUtils.checkIfEventExist("${widget.patientAppointmentHistoryResponseModel.appointmentNo}");
myAppointmentsViewModel.setAppointmentReminder(doesExist, widget.patientAppointmentHistoryResponseModel); // myAppointmentsViewModel.setAppointmentReminder(doesExist, widget.patientAppointmentHistoryResponseModel);
setState(() {}); // setState(() {});
} }
}); });
@ -148,41 +150,57 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
}); });
}, },
onCancelTap: () async { onCancelTap: () async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.cancellingAppointmentPleaseWait.tr(context: context)); showCommonBottomSheetWithoutHeight(
await myAppointmentsViewModel.cancelAppointment( title: LocaleKeys.notice.tr(context: context),
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, context,
onSuccess: (apiResponse) { child: Utils.getWarningWidget(
LoaderBottomSheet.hideLoader(); loadingText: LocaleKeys.cancelAppointmentConfirmMessage.tr(context: context),
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); isShowActionButtons: true,
myAppointmentsViewModel.getPatientAppointments(true, false); onCancelTap: () {
showCommonBottomSheetWithoutHeight( Navigator.of(context).pop();
context, },
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentCancelledSuccessfully.tr(context: context)), onConfirmTap: () async {
callBackFunc: () { Navigator.of(context).pop();
Navigator.of(context).pop(); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.cancellingAppointmentPleaseWait.tr(context: context));
}, myAppointmentsViewModel.onTabChange(0);
title: "", myAppointmentsViewModel.updateListWRTTab(0);
isCloseButtonVisible: true, await myAppointmentsViewModel.cancelAppointment(
isDismissible: false, patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
isFullScreen: false, onSuccess: (apiResponse) async {
); LoaderBottomSheet.hideLoader();
}, myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
onError: (err) { myAppointmentsViewModel.getPatientAppointments(true, false);
LoaderBottomSheet.hideLoader(); showCommonBottomSheet(
showCommonBottomSheetWithoutHeight( context,
context, child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentCancelledSuccessfully.tr(context: context)),
child: Utils.getErrorWidget(loadingText: err.toString()), callBackFunc: (str) {
callBackFunc: () {}, Navigator.of(context).pop();
title: "", },
isCloseButtonVisible: true, title: "",
isDismissible: false, isCloseButtonVisible: false,
isFullScreen: false, isDismissible: false,
); isFullScreen: false,
}); isAutoDismiss: true,
var isEventAddedOrRemoved = await CalenderUtilsNew.instance.checkAndRemove( id:"${widget.patientAppointmentHistoryResponseModel.appointmentNo}", ); height: ResponsiveExtension.screenHeight * 0.3,
setState(() { );
myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel); },
}); onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err.toString()),
callBackFunc: () {},
title: "", isCloseButtonVisible: true, isDismissible: false, isFullScreen: false);
});
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
// var isEventAddedOrRemoved = await CalenderUtilsNew.instance.checkAndRemove( id:"${widget.patientAppointmentHistoryResponseModel.appointmentNo}", );
// setState(() {
// myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
// });
}, },
onRescheduleTap: () async { onRescheduleTap: () async {
openDoctorScheduleCalendar(); openDoctorScheduleCalendar();
@ -229,16 +247,22 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false); // myAppointmentsViewModel.getPatientAppointments(true, false);
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: "", title: "",
context, context,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)),
callBackFunc: () { callBackFunc: () {
Navigator.of(context).pop(); Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
}, },
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: false,
isAutoDismiss: true
); );
}); });
}, },
@ -987,12 +1011,13 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL, doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL,
doctorTitle: widget.patientAppointmentHistoryResponseModel.doctorTitle, doctorTitle: widget.patientAppointmentHistoryResponseModel.doctorTitle,
name: widget.patientAppointmentHistoryResponseModel.doctorNameObj, name: widget.patientAppointmentHistoryResponseModel.doctorNameObj,
nationalityFlagURL: "https://hmgwebservices.com/Images/flag/SYR.png", nationalityFlagURL: "",
speciality: [], speciality: [],
clinicName: widget.patientAppointmentHistoryResponseModel.clinicName, clinicName: widget.patientAppointmentHistoryResponseModel.clinicName,
projectName: widget.patientAppointmentHistoryResponseModel.projectName, projectName: widget.patientAppointmentHistoryResponseModel.projectName,
); );
bookAppointmentsViewModel.setSelectedDoctor(doctor); bookAppointmentsViewModel.setSelectedDoctor(doctor);
bookAppointmentsViewModel.setIsPatientRescheduleAppointment(true);
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingDoctorSchedulePleaseWait.tr(context: context)); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingDoctorSchedulePleaseWait.tr(context: context));
await bookAppointmentsViewModel.getDoctorFreeSlots( await bookAppointmentsViewModel.getDoctorFreeSlots(
isBookingForLiveCare: false, isBookingForLiveCare: false,
@ -1036,17 +1061,19 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false); myAppointmentsViewModel.getPatientAppointments(true, false);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, }, title: "", height: ResponsiveExtension.screenHeight * 0.3, isAutoDismiss: true, isCloseButtonVisible: true, isDismissible: false, isFullScreen: false,
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: true,
isDismissible: false,
isFullScreen: false,
isSuccessDialog: true); isSuccessDialog: true);
}); });
// LoaderBottomSheet.hideLoader(); // LoaderBottomSheet.hideLoader();
case 15: case 15:
break; 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,
);
case 20: case 20:
myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true); myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true);
Navigator.of(context).push( Navigator.of(context).push(

@ -55,36 +55,40 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView( body: Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
title: LocaleKeys.appointmentsList.tr(context: context), return CollapsingListView(
child: SingleChildScrollView( title: LocaleKeys.appointmentsList.tr(context: context),
child: Column( child: SingleChildScrollView(
children: [ child: Column(
SizedBox(height: 16.h), children: [
CustomTabBar( SizedBox(height: 16.h),
activeTextColor: Color(0xffED1C2B), CustomTabBar(
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), initialIndex: myAppointmentsVM.selectedTabIndex,
tabs: [ activeTextColor: Color(0xffED1C2B),
CustomTabBarModel(null, LocaleKeys.allAppt.tr(context: context)), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
CustomTabBarModel(null, LocaleKeys.upcoming.tr(context: context)), tabs: [
CustomTabBarModel(null, LocaleKeys.completed.tr(context: context)), CustomTabBarModel(null, LocaleKeys.allAppt.tr(context: context)),
], CustomTabBarModel(null, LocaleKeys.upcoming.tr(context: context)),
onTabChange: (index) { CustomTabBarModel(null, LocaleKeys.completed.tr(context: context)),
setState(() { ],
expandedIndex = null; onTabChange: (index) {
}); setState(() {
myAppointmentsViewModel.onTabChange(index); expandedIndex = null;
myAppointmentsViewModel.updateListWRTTab(index); });
context.read<DateRangeSelectorRangeViewModel>().flush(); myAppointmentsViewModel.onTabChange(index);
}, myAppointmentsViewModel.updateListWRTTab(index);
).paddingSymmetrical(24.h, 0.h), context.read<DateRangeSelectorRangeViewModel>().flush();
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) { },
return getSelectedTabData(myAppointmentsVM.selectedTabIndex, myAppointmentsVM); ).paddingSymmetrical(24.h, 0.h),
}), // Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
], // return
getSelectedTabData(myAppointmentsVM.selectedTabIndex, myAppointmentsVM),
// }),
],
), ),
), ),
), );
}),
); );
} }
@ -144,7 +148,7 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
? myAppointmentsVM.patientAppointmentsViewList.length ? myAppointmentsVM.patientAppointmentsViewList.length
: 1, : 1,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final isExpanded = expandedIndex == index; final isExpanded = myAppointmentsVM.selectedTabIndex == 1 ? true : expandedIndex == index;
return myAppointmentsVM.isMyAppointmentsLoading return myAppointmentsVM.isMyAppointmentsLoading
? Container( ? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),

@ -218,8 +218,17 @@ class _MyDoctorsPageState extends State<MyDoctorsPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
AppCustomChipWidget(labelText: "${group.length} ${'doctors'}"), AppCustomChipWidget(
Icon(isExpanded ? Icons.expand_less : Icons.expand_more), richText: Row(
children: [
"${group.length} ".toText10(isEnglishOnly: true),
"${LocaleKeys.doctors.tr(context: context)} ".toText10()
],
),
// labelText: "${group.length} ${'doctors'}",
isEnglishOnly: true,
),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
], ],
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
@ -289,10 +298,10 @@ class _MyDoctorsPageState extends State<MyDoctorsPage> {
flex: 2, flex: 2,
child: CustomButton( child: CustomButton(
icon: AppAssets.view_report_icon, icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor, iconColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 1.0) : AppColors.primaryRedColor,
iconSize: 16.h, iconSize: 16.h,
text: LocaleKeys.viewProfile.tr(context: context), text: LocaleKeys.viewProfile.tr(context: context),
onPressed: () async { onPressed: doctor?.isActiveDoctorProfile == false ? () {} : () async {
bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel( bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel(
clinicID: doctor?.clinicID ?? 0, clinicID: doctor?.clinicID ?? 0,
projectID: doctor?.projectID ?? 0, projectID: doctor?.projectID ?? 0,
@ -323,9 +332,9 @@ class _MyDoctorsPageState extends State<MyDoctorsPage> {
); );
}); });
}, },
backgroundColor: AppColors.secondaryLightRedColor, backgroundColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 0.3) : AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor, borderColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 0.01) : AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor, textColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 1.0) : AppColors.primaryRedColor,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12, borderRadius: 12,

@ -148,8 +148,8 @@ class AppointmentCard extends StatelessWidget {
Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow), Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow),
SizedBox(height: 2.h), SizedBox(height: 2.h),
(isFoldable || isTablet) (isFoldable || isTablet)
? "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor) ? "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor, isEnglishOnly: true)
: "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(isBold: true, color: AppColors.textColor), : "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true),
], ],
), ),
).circle(100).toShimmer2(isShow: isLoading), ).circle(100).toShimmer2(isShow: isLoading),
@ -456,7 +456,7 @@ class AppointmentCard extends StatelessWidget {
doctorImageURL: patientAppointmentHistoryResponseModel.doctorImageURL, doctorImageURL: patientAppointmentHistoryResponseModel.doctorImageURL,
doctorTitle: patientAppointmentHistoryResponseModel.doctorTitle, doctorTitle: patientAppointmentHistoryResponseModel.doctorTitle,
name: patientAppointmentHistoryResponseModel.doctorNameObj, name: patientAppointmentHistoryResponseModel.doctorNameObj,
nationalityFlagURL: 'https://hmgwebservices.com/Images/flag/SYR.png', nationalityFlagURL: '',
speciality: [], speciality: [],
clinicName: patientAppointmentHistoryResponseModel.clinicName, clinicName: patientAppointmentHistoryResponseModel.clinicName,
projectName: patientAppointmentHistoryResponseModel.projectName, projectName: patientAppointmentHistoryResponseModel.projectName,

@ -1,19 +1,27 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.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/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.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/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/my_appointments/models/resp_models/patient_appointment_history_response_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/utils/appointment_type.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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
import 'package:hmg_patient_app_new/theme/colors.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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.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 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
class AppointmentDoctorCard extends StatelessWidget { class AppointmentDoctorCard extends StatelessWidget {
const AppointmentDoctorCard( const AppointmentDoctorCard(
{super.key, required this.patientAppointmentHistoryResponseModel, required this.onRescheduleTap, required this.onCancelTap, required this.onAskDoctorTap, this.renderWidgetForERDisplay = false}); {super.key, required this.patientAppointmentHistoryResponseModel, required this.onRescheduleTap, required this.onCancelTap, required this.onAskDoctorTap, this.renderWidgetForERDisplay = false});
@ -66,7 +74,7 @@ class AppointmentDoctorCard extends StatelessWidget {
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow), Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow),
SizedBox(height: 2.h), SizedBox(height: 2.h),
"${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? 0.0}".toText11(isBold: true, color: AppColors.textColor), "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? 0.0}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true),
], ],
), ),
).circle(100), ).circle(100),
@ -75,6 +83,7 @@ class AppointmentDoctorCard extends StatelessWidget {
), ),
SizedBox(width: 16.w), SizedBox(width: 16.w),
Expanded( Expanded(
flex: 9,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -120,6 +129,39 @@ class AppointmentDoctorCard extends StatelessWidget {
], ],
), ),
), ),
Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown).onPress(() async {
DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel();
selectedDoctor.doctorID = patientAppointmentHistoryResponseModel.doctorID;
selectedDoctor.doctorImageURL = patientAppointmentHistoryResponseModel.doctorImageURL;
selectedDoctor.name = patientAppointmentHistoryResponseModel.doctorNameObj;
selectedDoctor.doctorTitle = patientAppointmentHistoryResponseModel.doctorTitle;
selectedDoctor.nationalityFlagURL = "";
selectedDoctor.speciality = patientAppointmentHistoryResponseModel.doctorSpeciality;
selectedDoctor.clinicName = patientAppointmentHistoryResponseModel.clinicName;
selectedDoctor.projectName = patientAppointmentHistoryResponseModel.projectName;
selectedDoctor.clinicID = patientAppointmentHistoryResponseModel.clinicID;
selectedDoctor.projectID = patientAppointmentHistoryResponseModel.projectID;
getIt.get<BookAppointmentsViewModel>().setSelectedDoctor(selectedDoctor);
LoaderBottomSheet.showLoader();
await getIt.get<BookAppointmentsViewModel>().getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
})
], ],
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),

@ -84,7 +84,7 @@ class _SavedLogin extends State<SavedLogin> {
SizedBox(height: 16.h), SizedBox(height: 16.h),
appState.getSelectDeviceByImeiRespModelElement != null appState.getSelectDeviceByImeiRespModelElement != null
? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase ? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase
.toText26(isBold: true, height: 26 / 36, color: AppColors.textColor) .toText26(isBold: true, height: 26 / 36, color: AppColors.textColor, isEnglishOnly: true)
: SizedBox(), : SizedBox(),
SizedBox(height: 24.h), SizedBox(height: 24.h),
Container( Container(
@ -206,7 +206,7 @@ class _SavedLogin extends State<SavedLogin> {
}, },
backgroundColor: AppColors.primaryRedColor, backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor,
textColor: AppColors.textColor, textColor: Colors.white,
icon: AppAssets.sms), icon: AppAssets.sms),
), ),
Row( Row(

@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_liveca
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody; import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody;
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
@ -119,18 +120,30 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
if (appState.isAuthenticated) ...[ if (appState.isAuthenticated) ...[
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) { Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return myAppointmentsVM.isPatientMyDoctorsLoading return myAppointmentsVM.isPatientMyDoctorsLoading
? Column( ? SizedBox(
crossAxisAlignment: CrossAxisAlignment.center, height: 110.h,
children: [ child: ListView.separated(
Image.network( scrollDirection: Axis.horizontal,
itemCount: 5,
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.w, right: 24.w),
itemBuilder: (context, index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.network(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 64.w, width: 64.w,
height: 64.h, height: 64.h,
fit: BoxFit.cover, fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: true, radius: 50.r), ).circle(100).toShimmer2(isShow: true, radius: 50.r),
SizedBox(height: 8.h), SizedBox(height: 8.h),
("Dr. John Smith Smith Smith").toString().toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2).toShimmer2(isShow: true), ("Dr. John").toString().toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2).toShimmer2(isShow: true),
], ],
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 16.h),
),
) )
: myAppointmentsVM.patientMyDoctorsList.isEmpty : myAppointmentsVM.patientMyDoctorsList.isEmpty
? SizedBox() ? SizedBox()
@ -138,7 +151,25 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (appState.isAuthenticated) ...[], if (appState.isAuthenticated) ...[],
LocaleKeys.recentVisits.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h), Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.recentVisits.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
Row(
children: [
LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500),
SizedBox(width: 2.h),
Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h),
],
).paddingSymmetrical(24.h, 0.h).onPress(() {
Navigator.of(context).push(
CustomPageRoute(
page: MyDoctorsPage(),
),
);
}),
],
),
SizedBox(height: 16.h), SizedBox(height: 16.h),
SizedBox( SizedBox(
height: 110.h, height: 110.h,
@ -264,7 +295,46 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
// Show empty state or actual list // Show empty state or actual list
return myAppointmentsVM.patientFavouriteDoctorsList.isEmpty return myAppointmentsVM.patientFavouriteDoctorsList.isEmpty
? SizedBox() ? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
LocaleKeys.favouriteList.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
padding: EdgeInsets.all(16.h),
child: Column(
children: [
Center(
child: LocaleKeys.noFavouriteDoctors.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
),
SizedBox(height: 12.h),
CustomButton(
text: LocaleKeys.add.tr(context: context),
onPressed: () {
Navigator.of(context).push(CustomPageRoute(page: SearchDoctorByName()));
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 14.f,
fontWeight: FontWeight.w500,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor,
height: 40.h,
),
],
),
).paddingSymmetrical(24.w, 0.h),
SizedBox(height: 24.h),
],
)
: Column( : Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -659,7 +729,7 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
return SizedBox.shrink(); return SizedBox.shrink();
} }
void _handleSortByLocationToggle(bool value, AppointmentViaRegionViewmodel regionVM) { void _handleSortByLocationToggle(bool value, AppointmentViaRegionViewmodel regionVM) {
if (value) { if (value) {
final locationUtils = getIt.get<LocationUtils>(); final locationUtils = getIt.get<LocationUtils>();
locationUtils.getLocation( locationUtils.getLocation(

@ -33,17 +33,16 @@ class DoctorProfilePage extends StatelessWidget {
children: [ children: [
Expanded( Expanded(
child: CollapsingListView( child: CollapsingListView(
title: LocaleKeys.doctorProfile.tr(), title: LocaleKeys.profileOnlyText.tr(),
trailing: Consumer<BookAppointmentsViewModel>( trailing: appState.isAuthenticated ? Consumer<BookAppointmentsViewModel>(
builder: (context, viewModel, child) { builder: (context, viewModel, child) {
return SizedBox( return SizedBox(
width: 24.h, width: 24.h,
height: 24.h, height: 24.h,
child: Utils.buildSvgWithAssets( child: Icon(
icon: viewModel.isFavouriteDoctor ? AppAssets.bookmark_filled_icon : AppAssets.bookmark_icon, viewModel.isFavouriteDoctor ? Icons.bookmark : Icons.bookmark_border_outlined,
width: 24.h, size: 36.h,
height: 24.h, color: viewModel.isFavouriteDoctor ? AppColors.primaryRedColor : AppColors.textColor,
iconColor: viewModel.isFavouriteDoctor ? AppColors.primaryRedColor : AppColors.textColor,
).onPress(() async { ).onPress(() async {
viewModel.toggleFavouriteDoctor(); viewModel.toggleFavouriteDoctor();
await viewModel.insertFavouriteDoctor( await viewModel.insertFavouriteDoctor(
@ -68,7 +67,7 @@ class DoctorProfilePage extends StatelessWidget {
}), }),
); );
}, },
), ) : SizedBox.shrink(),
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -179,9 +178,10 @@ class DoctorProfilePage extends StatelessWidget {
SizedBox(height: 16.h), SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.docInfo.tr(context: context).toText14(weight: FontWeight.w600, color: AppColors.textColor), LocaleKeys.information.tr(context: context).toText14(weight: FontWeight.w600, color: AppColors.textColor),
SizedBox(height: 6.h), SizedBox(height: 6.h),
bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo!.toText12(fontWeight: FontWeight.w600, color: AppColors.greyTextColor), (bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo ?? "").trim().toText12(fontWeight: FontWeight.w600, color: AppColors.greyTextColor),
SizedBox(height: 24.h),
], ],
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),
), ),
@ -199,7 +199,12 @@ class DoctorProfilePage extends StatelessWidget {
bookAppointmentsViewModel.selectedDoctor.speciality = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty; bookAppointmentsViewModel.selectedDoctor.speciality = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty;
bookAppointmentsViewModel.selectedDoctor.specialityN = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty; bookAppointmentsViewModel.selectedDoctor.specialityN = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty;
bookAppointmentsViewModel.selectedDoctor.name = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName; bookAppointmentsViewModel.selectedDoctor.name = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName;
bookAppointmentsViewModel.selectedDoctor.name = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName; bookAppointmentsViewModel.selectedDoctor.doctorImageURL = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL;
bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL = bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL;
bookAppointmentsViewModel.selectedDoctor.clinicName = bookAppointmentsViewModel.doctorsProfileResponseModel.clinicDescription;
bookAppointmentsViewModel.selectedDoctor.projectName = bookAppointmentsViewModel.doctorsProfileResponseModel.projectName;
LoaderBottomSheet.showLoader(); LoaderBottomSheet.showLoader();
bookAppointmentsViewModel.isLiveCareSchedule bookAppointmentsViewModel.isLiveCareSchedule
? await bookAppointmentsViewModel.getLiveCareDoctorFreeSlots( ? await bookAppointmentsViewModel.getLiveCareDoctorFreeSlots(

@ -84,7 +84,7 @@ class LaserAppointment extends StatelessWidget {
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [ tabs: [
CustomTabBarModel(null, LocaleKeys.malE.tr()), CustomTabBarModel(null, LocaleKeys.malE.tr()),
CustomTabBarModel(null, "Female"), CustomTabBarModel(null, LocaleKeys.femaleGender.tr()),
], ],
onTabChange: (index) { onTabChange: (index) {
var viewmodel = context.read<BookAppointmentsViewModel>(); var viewmodel = context.read<BookAppointmentsViewModel>();
@ -93,6 +93,7 @@ class LaserAppointment extends StatelessWidget {
viewmodel.getLaserClinic(); viewmodel.getLaserClinic();
}, },
), ),
SizedBox(height: 8.h),
Selector<BookAppointmentsViewModel, int>( Selector<BookAppointmentsViewModel, int>(
selector: (_, vm) => vm.selectedBodyTypeIndex, selector: (_, vm) => vm.selectedBodyTypeIndex,
builder: (_, bodyType, __) { builder: (_, bodyType, __) {
@ -110,6 +111,7 @@ class LaserAppointment extends StatelessWidget {
}); });
}, },
), ),
SizedBox(height: 8.h),
Selector<BookAppointmentsViewModel, List<LaserBodyPart>>(selector: (_, vm) => vm.laserBodyPartsList, builder:(_, parts,__){ Selector<BookAppointmentsViewModel, List<LaserBodyPart>>(selector: (_, vm) => vm.laserBodyPartsList, builder:(_, parts,__){
return BodyPartsListing( return BodyPartsListing(
parts: parts, parts: parts,

@ -1,9 +1,12 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_export.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/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/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/laser_body_parts_data.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/laser_body_parts_data.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/laser_body_parts.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/laser_body_parts.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -70,7 +73,7 @@ class BodyPartsListing extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AspectRatio( AspectRatio(
aspectRatio: 97 / 97, aspectRatio: 130 / 120,
child: FittedBox( child: FittedBox(
fit: BoxFit.fitWidth, fit: BoxFit.fitWidth,
child: Stack( child: Stack(
@ -93,12 +96,12 @@ class BodyPartsListing extends StatelessWidget {
Container( Container(
width: 18.h, width: 18.h,
height: 18.h, height: 18.h,
child: Icon(Icons.done,
color: Colors.white, size: 12.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.primaryRedColor, color: AppColors.primaryRedColor,
borderRadius: BorderRadius.circular(30.h), borderRadius: BorderRadius.circular(30.h),
), ),
child: Icon(Icons.done,
color: Colors.white, size: 12.h),
), ),
], ],
), ),
@ -106,17 +109,19 @@ class BodyPartsListing extends StatelessWidget {
), ),
SizedBox(height: 6.h), SizedBox(height: 6.h),
Expanded( Expanded(
child: Text( child: Center(
context child: Column(
.read<BookAppointmentsViewModel>() children: [
.getLaserProcedureNameWRTLanguage(parts[index]), context.read<BookAppointmentsViewModel>().getLaserProcedureNameWRTLanguage(parts[index]).toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2),
style: TextStyle( Row(
fontSize: 12.f, mainAxisAlignment: MainAxisAlignment.center,
fontWeight: FontWeight.w600, children: [
color: Color(0xff2B353E), "${LocaleKeys.mins.tr()}: ".toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2),
letterSpacing: -0.48, parts[index].timeDuration!.toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2),
],
),
],
), ),
maxLines: 1,
), ),
), ),
], ],

@ -90,12 +90,12 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
.toString() .toString()
.toText16(isBold: true, maxlines: 1), .toText16(isBold: true, maxlines: 1),
SizedBox(width: 12.w), SizedBox(width: 12.w),
Image.network( (bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL != null && bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL!.isNotEmpty )? Image.network(
bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png",
width: 20.h, width: 20.h,
height: 15.h, height: 15.h,
fit: BoxFit.cover, fit: BoxFit.cover,
), ) : SizedBox.shrink(),
], ],
), ),
SizedBox(height: 2.h), SizedBox(height: 2.h),
@ -316,34 +316,42 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
} }
void initiateBookAppointment() async { void initiateBookAppointment() async {
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: LocaleKeys.bookingYourAppointment.tr(context: context)); // LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: bookAppointmentsViewModel.isPatientRescheduleAppointment ? LocaleKeys.reschedulingAppo.tr(context: context) : LocaleKeys.bookingYourAppointment.tr(context: context));
LoaderBottomSheet.showLoader(loadingText: bookAppointmentsViewModel.isPatientRescheduleAppointment ? LocaleKeys.reschedulingAppo.tr(context: context) : LocaleKeys.bookingYourAppointment.tr(context: context));
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
if (bookAppointmentsViewModel.isLiveCareSchedule) { if (bookAppointmentsViewModel.isLiveCareSchedule) {
await bookAppointmentsViewModel.insertSpecificAppointmentForLiveCare(onError: (err) { await bookAppointmentsViewModel.insertSpecificAppointmentForLiveCare(onError: (err) {
print(err.data["ErrorEndUserMessage"]); print(err.data["ErrorEndUserMessage"]);
LoadingUtils.hideFullScreenLoader(); LoaderBottomSheet.hideLoader();
}, onSuccess: (apiResp) async { }, onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader(); LoaderBottomSheet.hideLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async { await Future.delayed(Duration(milliseconds: 50)).then((value) async {
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.appointmentSuccess.tr());
await Future.delayed(Duration(milliseconds: 4000)).then((value) { showCommonBottomSheetWithoutHeight(
LoadingUtils.hideFullScreenLoader(); context,
bookAppointmentsViewModel.setIsLiveCareSchedule(false); child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()),
Navigator.pushAndRemoveUntil( callBackFunc: () {
context, bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false);
CustomPageRoute( bookAppointmentsViewModel.setIsLiveCareSchedule(false);
page: LandingNavigation(), Navigator.pushAndRemoveUntil(
), context,
(r) => false); CustomPageRoute(
}); page: LandingNavigation(),
),
(r) => false);
},
isFullScreen: false,
isCloseButtonVisible: false,
isAutoDismiss: true
);
}); });
}); });
} else { } else {
//TODO: Add patient Derma package check API Here //TODO: Add patient Derma package check API Here
await bookAppointmentsViewModel.insertSpecificAppointment(onError: (err) { await bookAppointmentsViewModel.insertSpecificAppointment(onError: (err) {
// print(err.data["ErrorEndUserMessage"]); // print(err.data["ErrorEndUserMessage"]);
LoadingUtils.hideFullScreenLoader(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context), title: LocaleKeys.notice.tr(context: context),
context, context,
@ -357,19 +365,25 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
isCloseButtonVisible: true, isCloseButtonVisible: true,
); );
}, onSuccess: (apiResp) async { }, onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader(); LoaderBottomSheet.hideLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async { await Future.delayed(Duration(milliseconds: 50)).then((value) async {
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); showCommonBottomSheetWithoutHeight(
await Future.delayed(Duration(milliseconds: 4000)).then((value) { context,
LoadingUtils.hideFullScreenLoader(); child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
bookAppointmentsViewModel.setIsLiveCareSchedule(false); callBackFunc: () {
Navigator.pushAndRemoveUntil( bookAppointmentsViewModel.setIsLiveCareSchedule(false);
context, bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false);
CustomPageRoute( Navigator.pushAndRemoveUntil(
page: LandingNavigation(), context,
), CustomPageRoute(
(r) => false); page: LandingNavigation(),
}); ),
(r) => false);
},
isFullScreen: false,
isCloseButtonVisible: false,
isAutoDismiss: true
);
}); });
}); });
} }

@ -1132,6 +1132,7 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID?.toString() ?? ""; id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID?.toString() ?? "";
} }
if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) { if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) {
bookAppointmentsViewModel.setProjectID(id);
if (appState.isAuthenticated) { if (appState.isAuthenticated) {
initDentalAppointment(); initDentalAppointment();
return SizedBox.shrink(); return SizedBox.shrink();

@ -76,7 +76,7 @@ class DoctorCard extends StatelessWidget {
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, applyThemeColor: false), Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, applyThemeColor: false),
SizedBox(height: 2.h), SizedBox(height: 2.h),
"${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor), "${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true),
], ],
), ),
).circle(100).toShimmer2(isShow: isLoading), ).circle(100).toShimmer2(isShow: isLoading),
@ -134,6 +134,7 @@ class DoctorCard extends StatelessWidget {
// richText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)) // richText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot))
// .toText10(isEnglishOnly: true, color: AppColors.textColor), // .toText10(isEnglishOnly: true, color: AppColors.textColor),
// backgroundColor: AppColors.successColor, // backgroundColor: AppColors.successColor,
isEnglishOnly: true,
textColor: AppColors.textColor, textColor: AppColors.textColor,
).toShimmer2(isShow: isLoading) ).toShimmer2(isShow: isLoading)
: SizedBox.shrink() : SizedBox.shrink()

@ -20,7 +20,7 @@ class DoctorRatingDetails extends StatelessWidget {
: Column( : Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
bookAppointmentsVM.doctorsProfileResponseModel.actualDoctorRate!.ceilToDouble().toString().toText44(isBold: true, isEnglishOnly: true), bookAppointmentsVM.doctorsProfileResponseModel.decimalDoctorRate!.toString().toText44(isBold: true, isEnglishOnly: true),
SizedBox(height: 4.h), SizedBox(height: 4.h),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,

@ -385,7 +385,7 @@ class _CmcSelectionReviewPageState extends State<CmcSelectionReviewPage> {
isCloseButtonVisible: false, isCloseButtonVisible: false,
isDismissible: false, isDismissible: false,
isFullScreen: false, isFullScreen: false,
); isAutoDismiss: true);
} }
void _handleConfirm() { void _handleConfirm() {

@ -39,8 +39,8 @@ class ContactUs extends StatelessWidget {
checkInOptionCard( checkInOptionCard(
AppAssets.call_fill, AppAssets.call_fill,
LocaleKeys.callNow.tr(), LocaleKeys.callNow.tr(),
// LocaleKeys.viewNearestHMGLocationsviewNearestHMGLocations.tr(), LocaleKeys.callForAssistance.tr(),
"Call for immediate assistance", // "Call for immediate assistance",
).onPress(() { ).onPress(() {
launchUrl(Uri.parse("tel://" + "+966 92 006 6666")); launchUrl(Uri.parse("tel://" + "+966 92 006 6666"));
}), }),
@ -83,6 +83,8 @@ class ContactUs extends StatelessWidget {
LocaleKeys.liveChatWithHMG.tr(), LocaleKeys.liveChatWithHMG.tr(),
).onPress(() { ).onPress(() {
if (getIt.get<AppState>().isAuthenticated) { if (getIt.get<AppState>().isAuthenticated) {
Navigator.of(context).pop();
contactUsViewModel.getLiveChatProjectsList();
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: LiveChatPage(), page: LiveChatPage(),
@ -93,9 +95,9 @@ class ContactUs extends StatelessWidget {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.liveChat.tr(), serviceName: LocaleKeys.liveChat.tr(),
serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, serviceHeader: LocaleKeys.liveChatServiceHeader.tr(),
serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, serviceDescription: LocaleKeys.liveChatServiceDescription.tr(),
serviceImage: AppAssets.eReferralService), serviceImage: AppAssets.livechatService),
), ),
); );
} }

@ -121,7 +121,9 @@ class LiveChatPage extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
("${appState.isArabic() ? contactUsVM.liveChatProjectsList[index].projectNameN! : contactUsVM.liveChatProjectsList[index].projectName!}\n${contactUsVM.liveChatProjectsList[index].distanceInKilometers!} KM") ("${appState.isArabic() ? contactUsVM.liveChatProjectsList[index].projectNameN! : contactUsVM.liveChatProjectsList[index].projectName!}"
// "\n${contactUsVM.liveChatProjectsList[index].distanceInKilometers!} KM"
)
.toText14(isBold: true, color: contactUsVM.selectedLiveChatProjectIndex == index ? Colors.white : AppColors.textColor), .toText14(isBold: true, color: contactUsVM.selectedLiveChatProjectIndex == index ? Colors.white : AppColors.textColor),
Transform.flip( Transform.flip(
flipX: getIt.get<AppState>().isArabic(), flipX: getIt.get<AppState>().isArabic(),

@ -21,6 +21,7 @@ class RrtRequestTypeSelect extends StatelessWidget {
return Column( return Column(
children: [ children: [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
LocaleKeys.rapidResponseTeam.tr(context: context).toText20(color: AppColors.textColor, isBold: true), LocaleKeys.rapidResponseTeam.tr(context: context).toText20(color: AppColors.textColor, isBold: true),
SizedBox( SizedBox(
@ -32,10 +33,9 @@ class RrtRequestTypeSelect extends StatelessWidget {
borderRadius: 20.r, borderRadius: 20.r,
), ),
child: OptionSelection(context)), child: OptionSelection(context)),
SizedBox( SizedBox(height: 16.h),
height: 24.h,
),
termsAndCondition(context, emergencyServicesVM), termsAndCondition(context, emergencyServicesVM),
SizedBox(height: 16.h),
], ],
).paddingSymmetrical(24.w, 0.h), ).paddingSymmetrical(24.w, 0.h),
bottomPriceContent(context, emergencyServicesVM) bottomPriceContent(context, emergencyServicesVM)
@ -122,15 +122,7 @@ class RrtRequestTypeSelect extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Row( Utils.getPaymentMethods(),
spacing: 6.w,
children: [
Image.asset(AppAssets.mada, width: 24.h, height: 24.h),
Image.asset(AppAssets.visa, width: 24.h, height: 24.h),
Image.asset(AppAssets.mastercard, width: 24.h, height: 24.h),
Image.asset(AppAssets.applePay, width: 24.h, height: 24.h),
],
),
Column( Column(
children: [ children: [
Divider( Divider(
@ -149,7 +141,9 @@ class RrtRequestTypeSelect extends StatelessWidget {
CustomButton(text: LocaleKeys.next.tr(), onPressed: () { CustomButton(text: LocaleKeys.next.tr(), onPressed: () {
Navigator.pop(context); Navigator.pop(context);
emergencyServicesVM.openRRT(); emergencyServicesVM.openRRT();
}) },
isDisabled: !emergencyServicesVM.agreedToTermsAndCondition,
)
], ],
).paddingAll(24.h), ).paddingAll(24.h),
), ),
@ -236,7 +230,6 @@ class RrtRequestTypeSelect extends StatelessWidget {
value: emergencyServicesVM.agreedToTermsAndCondition, value: emergencyServicesVM.agreedToTermsAndCondition,
checkColor: AppColors.whiteColor, checkColor: AppColors.whiteColor,
fillColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) { fillColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) {
print("the state is ${states}");
if (states.contains(WidgetState.selected)) { if (states.contains(WidgetState.selected)) {
return AppColors.errorColor; return AppColors.errorColor;
} }

@ -92,7 +92,10 @@ class _EmergencyServicesPageState extends State<EmergencyServicesPage> {
), ),
), ),
SizedBox(width: 12.h), SizedBox(width: 12.h),
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h),
),
], ],
).onPress(() { ).onPress(() {
if(appState.isAuthenticated) { if(appState.isAuthenticated) {
@ -158,8 +161,8 @@ class _EmergencyServicesPageState extends State<EmergencyServicesPage> {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.emergencyServices.tr(), serviceName: LocaleKeys.emergencyServices.tr(),
serviceHeader: "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly".needTranslation, serviceHeader: LocaleKeys.emergencyServiceHeader.tr(),
serviceDescription: "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.".needTranslation, serviceDescription: LocaleKeys.emergencyServiceDescription.tr(),
serviceImage: AppAssets.emergencyService), serviceImage: AppAssets.emergencyService),
), ),
); );
@ -188,7 +191,10 @@ class _EmergencyServicesPageState extends State<EmergencyServicesPage> {
), ),
), ),
SizedBox(width: 12.h), SizedBox(width: 12.h),
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h),
),
], ],
).onPress(() { ).onPress(() {
context.read<EmergencyServicesViewModel>().navigateToNearestERPage(); context.read<EmergencyServicesViewModel>().navigateToNearestERPage();
@ -216,7 +222,7 @@ class _EmergencyServicesPageState extends State<EmergencyServicesPage> {
), ),
), ),
SizedBox(width: 12.h), SizedBox(width: 12.h),
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h)),
], ],
).onPress(() { ).onPress(() {
if(appState.isAuthenticated) { if(appState.isAuthenticated) {
@ -308,8 +314,8 @@ class _EmergencyServicesPageState extends State<EmergencyServicesPage> {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.emergencyServices.tr(), serviceName: LocaleKeys.emergencyServices.tr(),
serviceHeader: "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly".needTranslation, serviceHeader: LocaleKeys.emergencyServiceHeader.tr(),
serviceDescription: "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.".needTranslation, serviceDescription: LocaleKeys.emergencyServiceDescription.tr(),
serviceImage: AppAssets.emergencyService), serviceImage: AppAssets.emergencyService),
), ),
); );
@ -338,7 +344,7 @@ class _EmergencyServicesPageState extends State<EmergencyServicesPage> {
), ),
), ),
SizedBox(width: 12.h), SizedBox(width: 12.h),
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h)),
], ],
).onPress(() { ).onPress(() {
if(appState.isAuthenticated) { if(appState.isAuthenticated) {
@ -408,8 +414,8 @@ class _EmergencyServicesPageState extends State<EmergencyServicesPage> {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.emergencyServices.tr(), serviceName: LocaleKeys.emergencyServices.tr(),
serviceHeader: "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly".needTranslation, serviceHeader: LocaleKeys.emergencyServiceHeader.tr(),
serviceDescription: "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.".needTranslation, serviceDescription: LocaleKeys.emergencyServiceDescription.tr(),
serviceImage: AppAssets.emergencyService), serviceImage: AppAssets.emergencyService),
), ),
); );

@ -320,6 +320,7 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
}, },
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
isAutoDismiss: true
); );
}, },
onError: (err) { onError: (err) {

@ -12,6 +12,7 @@ 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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart'; import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart'; import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/hmg_services_component_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/hmg_services_component_model.dart';
@ -20,6 +21,7 @@ import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_mode
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart'; import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/blood_donation/blood_donation_page.dart'; import 'package:hmg_patient_app_new/presentation/blood_donation/blood_donation_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/contact_us.dart'; import 'package:hmg_patient_app_new/presentation/contact_us/contact_us.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/emergency_services_page.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/emergency_services_page.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart';
@ -80,7 +82,11 @@ class ServicesPage extends StatelessWidget {
AppAssets.appointment_calendar_icon, AppAssets.appointment_calendar_icon,
bgColor: AppColors.bookAppointment, bgColor: AppColors.bookAppointment,
true, true,
route: AppRoutes.bookAppointmentPage, route: null,
onTap: () {
getIt.get<BookAppointmentsViewModel>().onTabChanged(0);
Navigator.of(getIt<NavigationService>().navigatorKey.currentContext!).push(CustomPageRoute(page: BookAppointmentPage()));
}
), ),
HmgServicesComponentModel(5, LocaleKeys.completeCheckup.tr(), "", AppAssets.comprehensiveCheckup, bgColor: AppColors.bgGreenColor, true, route: null, onTap: () async { HmgServicesComponentModel(5, LocaleKeys.completeCheckup.tr(), "", AppAssets.comprehensiveCheckup, bgColor: AppColors.bgGreenColor, true, route: null, onTap: () async {
if (getIt.get<AppState>().isAuthenticated) { if (getIt.get<AppState>().isAuthenticated) {
@ -90,8 +96,8 @@ class ServicesPage extends StatelessWidget {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.comprehensiveMedicalCheckup.tr(), serviceName: LocaleKeys.comprehensiveMedicalCheckup.tr(),
serviceHeader: "Complete Health Checkup: Advanced diagnostics, Detailed Health insights".needTranslation, serviceHeader: LocaleKeys.cmcServiceHeader.tr(),
serviceDescription: "Get detailed insights into your health with our advanced diagnostics. Understand your body better for a healthier future.".needTranslation, serviceDescription: LocaleKeys.cmcServiceDescription.tr(),
serviceImage: AppAssets.cmcService), serviceImage: AppAssets.cmcService),
), ),
); );
@ -150,8 +156,8 @@ class ServicesPage extends StatelessWidget {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.eReferralServices.tr(), serviceName: LocaleKeys.eReferralServices.tr(),
serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, serviceHeader: LocaleKeys.eReferralServiceHeader.tr(),
serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, serviceDescription: LocaleKeys.eReferralServiceDescription.tr(),
serviceImage: AppAssets.eReferralService), serviceImage: AppAssets.eReferralService),
), ),
); );
@ -177,8 +183,8 @@ class ServicesPage extends StatelessWidget {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.bloodDonation.tr(), serviceName: LocaleKeys.bloodDonation.tr(),
serviceHeader: "Give Blood, Save Lives. Your donation makes a difference.".needTranslation, serviceHeader: LocaleKeys.bloodDonationServiceHeader.tr(),
serviceDescription: "Donate blood, empower lives. Your contribution creates hope. Join our blood drive and be a lifeline for those in need. Every drop counts!".needTranslation, serviceDescription: LocaleKeys.bloodDonationServiceDescription.tr(),
serviceImage: AppAssets.bloodDonationService), serviceImage: AppAssets.bloodDonationService),
), ),
); );
@ -270,9 +276,9 @@ class ServicesPage extends StatelessWidget {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.healthTrackers.tr(), serviceName: LocaleKeys.healthTrackers.tr(),
serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, serviceHeader: LocaleKeys.healthTrackersServiceHeader.tr(),
serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, serviceDescription: LocaleKeys.healthTrackersServiceDescription.tr(),
serviceImage: AppAssets.eReferralService), serviceImage: AppAssets.healthTrackersService),
), ),
); );
} }
@ -311,8 +317,8 @@ class ServicesPage extends StatelessWidget {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.waterConsumption.tr(), serviceName: LocaleKeys.waterConsumption.tr(),
serviceHeader: "Stay Hydrated, Stay Healthy. Track your daily water intake with ease.".needTranslation, serviceHeader: LocaleKeys.waterConsumptionServiceHeader.tr(),
serviceDescription: "Quench your thirst, track your health. Effortlessly monitor your daily water intake with our user-friendly app, ensuring optimal hydration and well-being.".needTranslation, serviceDescription: LocaleKeys.waterConsumptionServiceDescription.tr(),
serviceImage: AppAssets.waterConsumptionService), serviceImage: AppAssets.waterConsumptionService),
), ),
); );
@ -353,9 +359,9 @@ class ServicesPage extends StatelessWidget {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.smartWatches.tr(), serviceName: LocaleKeys.smartWatches.tr(),
serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, serviceHeader: LocaleKeys.smartWatchServiceHeader.tr(),
serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, serviceDescription: LocaleKeys.smartWatchServiceDescription.tr(),
serviceImage: AppAssets.eReferralService), serviceImage: AppAssets.smartWatchService),
), ),
); );
} }

@ -1,6 +1,8 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.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/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/home/data/service_card_data.dart'; import 'package:hmg_patient_app_new/presentation/home/data/service_card_data.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -123,8 +125,8 @@ class LandingPageData {
ServiceCardData( ServiceCardData(
serviceName: "my_doctors", serviceName: "my_doctors",
icon: AppAssets.my_doctors_icon, icon: AppAssets.my_doctors_icon,
title: "My", title: getIt.get<AppState>().isArabic() ? LocaleKeys.myDoctor : "My",
subtitle: "Doctors", subtitle: getIt.get<AppState>().isArabic() ? "" : "Doctors",
backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor, iconColor: AppColors.blackColor,
textColor: AppColors.blackColor, textColor: AppColors.blackColor,

@ -133,22 +133,22 @@ class _LandingPageState extends State<LandingPage> {
// Commented as per new requirement to remove rating popup from the app // Commented as per new requirement to remove rating popup from the app
// if(!appState.isRatedVisible) { if(!appState.isRatedVisible) {
// appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) { appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) {
// if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) { if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) {
// appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!, appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!,
// onSuccess: ((response) { onSuccess: ((response) {
// appointmentRatingViewModel.setClinicOrDoctor(false); appointmentRatingViewModel.setClinicOrDoctor(false);
// appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation); appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr(context: context));
// appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation); appointmentRatingViewModel.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context));
// openLastRating(); openLastRating();
// appState.setRatedVisible(true); appState.setRatedVisible(true);
// }), }),
// ); );
// } }
// }, },
// ); );
// } }
} }
}); });
super.initState(); super.initState();
@ -173,7 +173,7 @@ class _LandingPageState extends State<LandingPage> {
children: [ children: [
SingleChildScrollView( SingleChildScrollView(
padding: EdgeInsets.only( padding: EdgeInsets.only(
top: (!insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown) top: (appState.isAuthenticated && !insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown)
? (MediaQuery.paddingOf(context).top + 70.h) ? (MediaQuery.paddingOf(context).top + 70.h)
: kToolbarHeight + 0.h, : kToolbarHeight + 0.h,
bottom: 24), bottom: 24),
@ -365,6 +365,8 @@ class _LandingPageState extends State<LandingPage> {
), ),
], ],
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() {
myAppointmentsViewModel.onTabChange(0);
myAppointmentsViewModel.updateListWRTTab(0);
Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage())); Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage()));
}), }),
Consumer3<MyAppointmentsViewModel, ImmediateLiveCareViewModel, TodoSectionViewModel>( Consumer3<MyAppointmentsViewModel, ImmediateLiveCareViewModel, TodoSectionViewModel>(
@ -727,7 +729,7 @@ class _LandingPageState extends State<LandingPage> {
], ],
), ),
), ),
(!insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown) (appState.isAuthenticated && !insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown)
? Container( ? Container(
height: MediaQuery.paddingOf(context).top + 50.h, height: MediaQuery.paddingOf(context).top + 50.h,
decoration: ShapeDecoration( decoration: ShapeDecoration(

@ -63,6 +63,7 @@ class ServiceInfoPage extends StatelessWidget {
child: Image.asset( child: Image.asset(
serviceImage, serviceImage,
fit: BoxFit.fitHeight, fit: BoxFit.fitHeight,
height: 480.h,
width: 520.w, width: 520.w,
), ),
), ),

@ -148,8 +148,8 @@ class LargeServiceCard extends StatelessWidget {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.homeHealthCare.tr(), serviceName: LocaleKeys.homeHealthCare.tr(),
serviceHeader: "Your Health, Elevated. Premium care, delivered to your doorstep.", serviceHeader: LocaleKeys.homeHealthCareServiceHeader.tr(),
serviceDescription: "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.", serviceDescription: LocaleKeys.homeHealthCareServiceDescription.tr(),
serviceImage: AppAssets.homeHealthCareService), serviceImage: AppAssets.homeHealthCareService),
), ),
); );
@ -295,8 +295,8 @@ class FadedLargeServiceCard extends StatelessWidget {
CustomPageRoute( CustomPageRoute(
page: ServiceInfoPage( page: ServiceInfoPage(
serviceName: LocaleKeys.homeHealthCare.tr(), serviceName: LocaleKeys.homeHealthCare.tr(),
serviceHeader: "Your Health, Elevated. Premium care, delivered to your doorstep.".needTranslation, serviceHeader: LocaleKeys.homeHealthCareServiceHeader.tr(),
serviceDescription: "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.".needTranslation, serviceDescription: LocaleKeys.homeHealthCareServiceDescription.tr(),
serviceImage: AppAssets.homeHealthCareService), serviceImage: AppAssets.homeHealthCareService),
), ),
); );

@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.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/core/utils/utils.dart';
@ -42,7 +43,11 @@ class WelcomeWidget extends StatelessWidget {
children: [ children: [
Icon(Icons.menu, color: AppColors.textColor).onPress(() { Icon(Icons.menu, color: AppColors.textColor).onPress(() {
getIt.get<ProfileSettingsViewModel>().getProfileSettings(); getIt.get<ProfileSettingsViewModel>().getProfileSettings();
Navigator.of(context).push(springPageRoute(ProfileSettings())); Navigator.of(context).push(
CustomPageRoute(
page: ProfileSettings(),
),
);
}), }),
Image.asset(imageUrl, width: 40, height: 40), Image.asset(imageUrl, width: 40, height: 40),
Column( Column(
@ -58,7 +63,17 @@ class WelcomeWidget extends StatelessWidget {
children: [ children: [
Flexible(child: name.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1, height: 1, isEnglishOnly: true)), Flexible(child: name.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1, height: 1, isEnglishOnly: true)),
// Icon(Icons.keyboard_arrow_down, size: 20, color: AppColors.greyTextColor), // Icon(Icons.keyboard_arrow_down, size: 20, color: AppColors.greyTextColor),
Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, height: 22.h, width: 22.w) Transform.flip(
flipX: getIt.get<AppState>().isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.arrowRight,
iconColor: AppColors.blackColor,
width: 22.w,
height: 22.h,
fit: BoxFit.contain,
)
),
// Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, height: 22.h, width: 22.w)
], ],
), ),
], ],

@ -74,6 +74,7 @@ class _InsuranceHomePageState extends State<InsuranceHomePage> {
return Column( return Column(
children: [ children: [
PatientInsuranceCard( PatientInsuranceCard(
isShowButtons: index == 0,
insuranceCardDetailsModel: insuranceVM.patientInsuranceList[index], insuranceCardDetailsModel: insuranceVM.patientInsuranceList[index],
isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo))), isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo))),
SizedBox( SizedBox(

@ -130,6 +130,7 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget {
}, },
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: false, isCloseButtonVisible: false,
isAutoDismiss: true
); );
// Future.delayed(Duration(milliseconds: 2000)).then((value) async { // Future.delayed(Duration(milliseconds: 2000)).then((value) async {
// Navigator.pop(context); // Navigator.pop(context);

@ -20,10 +20,11 @@ import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
class PatientInsuranceCard extends StatelessWidget { class PatientInsuranceCard extends StatelessWidget {
PatientInsuranceCard({super.key, required this.insuranceCardDetailsModel, required this.isInsuranceExpired}); PatientInsuranceCard({super.key, required this.insuranceCardDetailsModel, required this.isInsuranceExpired, this.isShowButtons = true});
PatientInsuranceDetailsResponseModel insuranceCardDetailsModel; PatientInsuranceDetailsResponseModel insuranceCardDetailsModel;
bool isInsuranceExpired = false; bool isInsuranceExpired = false;
bool isShowButtons = true;
late InsuranceViewModel insuranceViewModel; late InsuranceViewModel insuranceViewModel;
late AppState appState; late AppState appState;
@ -60,7 +61,7 @@ class PatientInsuranceCard extends StatelessWidget {
), ),
], ],
), ),
AppCustomChipWidget( isShowButtons ? AppCustomChipWidget(
icon: isCurrentPatientInsuranceExpired(insuranceCardDetailsModel.cardValidTo!) icon: isCurrentPatientInsuranceExpired(insuranceCardDetailsModel.cardValidTo!)
? AppAssets.cancel_circle_icon ? AppAssets.cancel_circle_icon
: insuranceViewModel.isInsuranceActive : insuranceViewModel.isInsuranceActive
@ -120,7 +121,7 @@ class PatientInsuranceCard extends StatelessWidget {
? AppColors.successColor.withOpacity(0.1) ? AppColors.successColor.withOpacity(0.1)
: AppColors.warningColorYellow.withOpacity(0.1), : AppColors.warningColorYellow.withOpacity(0.1),
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: insuranceViewModel.isInsuranceActive ? 6.w : 0.w), labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: insuranceViewModel.isInsuranceActive ? 6.w : 0.w),
).toShimmer2(isShow: insuranceViewModel.isInsuranceLoading), ).toShimmer2(isShow: insuranceViewModel.isInsuranceLoading) : SizedBox.shrink(),
// AppCustomChipWidget( // AppCustomChipWidget(
// icon: isInsuranceExpired ? AppAssets.cancel_circle_icon : AppAssets.insurance_active_icon, // icon: isInsuranceExpired ? AppAssets.cancel_circle_icon : AppAssets.insurance_active_icon,
// labelText: isInsuranceExpired ? LocaleKeys.insuranceExpired.tr(context: context) : LocaleKeys.insuranceActive.tr(context: context), // labelText: isInsuranceExpired ? LocaleKeys.insuranceExpired.tr(context: context) : LocaleKeys.insuranceActive.tr(context: context),
@ -168,7 +169,7 @@ class PatientInsuranceCard extends StatelessWidget {
), ),
SizedBox(height: 10.h), SizedBox(height: 10.h),
isInsuranceExpired isInsuranceExpired
? CustomButton( ? isShowButtons ? CustomButton(
icon: AppAssets.update_insurance_card_icon, icon: AppAssets.update_insurance_card_icon,
iconColor: AppColors.warningColorYellow, iconColor: AppColors.warningColorYellow,
iconSize: 15.h, iconSize: 15.h,
@ -194,7 +195,7 @@ class PatientInsuranceCard extends StatelessWidget {
borderRadius: 12, borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h, height: 40.h,
) ) : SizedBox.shrink()
: Container(), : Container(),
], ],
), ),

@ -45,41 +45,55 @@ class _AlphabetScrollPageState extends State<AlphabeticScroll> {
void initState() { void initState() {
super.initState(); super.initState();
scheduleMicrotask((){ scheduleMicrotask((){
for(var char in widget.alpahbetsAvailable){ _rebuildData();
data[char] = widget.details.where((element)=>element.description?.toLowerCase().startsWith(char.toLowerCase()) == true).toList();
}
setState((){});
}); });
itemPositionsListener.itemPositions.addListener((){ itemPositionsListener.itemPositions.addListener(_onPositionChanged);
}
@override
void didUpdateWidget(covariant AlphabeticScroll oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.details != widget.details || oldWidget.alpahbetsAvailable != widget.alpahbetsAvailable) {
_rebuildData();
}
}
final positions = itemPositionsListener.itemPositions.value; void _rebuildData() {
data.clear();
for (var char in widget.alpahbetsAvailable) {
data[char] = widget.details
.where((element) => element.description?.toLowerCase().startsWith(char.toLowerCase()) == true)
.toList();
}
if (_activeIndex >= widget.alpahbetsAvailable.length) {
_activeIndex = 0;
}
setState(() {});
}
if (positions.isEmpty) return; void _onPositionChanged() {
final positions = itemPositionsListener.itemPositions.value;
// Get FIRST visible item (top-most) if (positions.isEmpty) return;
final firstVisible = positions
.where((p) => p.itemTrailingEdge > 0) // visible
.reduce((min, p) =>
p.itemLeadingEdge < min.itemLeadingEdge ? p : min);
if(_activeIndex == firstVisible.index) return ; // Get FIRST visible item (top-most)
setState(() { final firstVisible = positions
_activeIndex = firstVisible.index; .where((p) => p.itemTrailingEdge > 0) // visible
}); .reduce((min, p) =>
p.itemLeadingEdge < min.itemLeadingEdge ? p : min);
print("Active index = $_activeIndex"); if(_activeIndex == firstVisible.index) return ;
setState(() {
_activeIndex = firstVisible.index;
}); });
print("Active index = $_activeIndex");
} }
@override @override
void dispose() { void dispose() {
itemPositionsListener.itemPositions.removeListener((){ itemPositionsListener.itemPositions.removeListener(_onPositionChanged);
});
super.dispose(); super.dispose();
} }
void _scrollToLetter(String letter) async { void _scrollToLetter(String letter) async {
@ -145,7 +159,6 @@ class _AlphabetScrollPageState extends State<AlphabeticScroll> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children:List.generate(widget.alpahbetsAvailable.length, (i) { children:List.generate(widget.alpahbetsAvailable.length, (i) {
final isActive = (i == _activeIndex); final isActive = (i == _activeIndex);
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
setState(() => _activeIndex = i); setState(() => _activeIndex = i);
@ -161,7 +174,7 @@ class _AlphabetScrollPageState extends State<AlphabeticScroll> {
child: Opacity( child: Opacity(
opacity: isActive ? 1.0 : 0.5, opacity: isActive ? 1.0 : 0.5,
child: widget.alpahbetsAvailable[i].toUpperCase().toText14( child: widget.alpahbetsAvailable[i].toUpperCase().toText14(
color: !isActive ? AppColors.greyTextColor : AppColors.primaryRedColor color: !isActive ? AppColors.greyTextColor : AppColors.primaryRedColor, isEnglishOnly: true
), ),
), ),
); );

@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'dart:ui' as ui;
class LabOrderByTest extends StatelessWidget { class LabOrderByTest extends StatelessWidget {
final VoidCallback onTap; final VoidCallback onTap;
@ -36,7 +37,7 @@ class LabOrderByTest extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
'${tests!.description}'.toText16(isBold: true), '${tests!.description}'.toText16(isBold: true, isEnglishOnly: true),
SizedBox(height: 4.h), SizedBox(height: 4.h),
(appState.isArabic() ? tests!.testDescriptionAr : tests!.testDescriptionEn)!.toText12(fontWeight: FontWeight.w500), (appState.isArabic() ? tests!.testDescriptionAr : tests!.testDescriptionEn)!.toText12(fontWeight: FontWeight.w500),
SizedBox(height: 8.h), SizedBox(height: 8.h),
@ -44,7 +45,13 @@ class LabOrderByTest extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
richText: '${"${LocaleKeys.lastTested.tr(context: context)}:"} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false)}'.toText12(fontWeight: FontWeight.w500), richText: Row(
children: [
"${LocaleKeys.lastTested.tr(context: context)}: ".toText12(fontWeight: FontWeight.w500),
Directionality(
textDirection: ui.TextDirection.ltr,child: DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false).toText12(fontWeight: FontWeight.w500, isEnglishOnly: true))
],
),
backgroundColor: AppColors.greyLightColor, backgroundColor: AppColors.greyLightColor,
textColor: AppColors.textColor, textColor: AppColors.textColor,
), ),

File diff suppressed because one or more lines are too long

@ -27,7 +27,7 @@ class LabResultList extends StatelessWidget {
shrinkWrap: true,itemCount: list.length,itemBuilder: (____, index) { shrinkWrap: true,itemCount: list.length,itemBuilder: (____, index) {
var labItem = list[index]; var labItem = list[index];
return LabOrderResultItem(onTap: () { return LabOrderResultItem(onTap: () {
model.getPatientLabResult(model.currentlySelectedPatientOrder!, labItem.description ?? "", labItem.testShortDescription!, labItem.uOM ?? ""); model.getPatientLabResult(model.currentlySelectedPatientOrder!, labItem.description ?? "", labItem.testShortDescription ?? "", labItem.uOM ?? "");
}, },
tests: labItem, tests: labItem,
index: index, index: index,

@ -44,7 +44,7 @@ class LabOrderResultItem extends StatelessWidget {
), ),
// (tests!.packageShortDescription ?? "").toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), // (tests!.packageShortDescription ?? "").toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
((tests!.testShortDescription != null && tests!.testShortDescription!.isNotEmpty) ? tests!.testShortDescription : tests!.packageShortDescription)! (((tests!.testShortDescription != null && tests!.testShortDescription!.isNotEmpty) ? tests!.testShortDescription : tests!.packageShortDescription) ?? "")
.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), .toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
SizedBox(height: 12.h), SizedBox(height: 12.h),
Directionality( Directionality(

@ -23,6 +23,7 @@ import 'package:hmg_patient_app_new/widgets/graph/custom_graph.dart';
import 'package:provider/provider.dart' show Consumer, Provider, ReadContext, Selector; import 'package:provider/provider.dart' show Consumer, Provider, ReadContext, Selector;
import '../../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight; import '../../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight;
import 'dart:ui' as ui;
class LabResultDetails extends StatelessWidget { class LabResultDetails extends StatelessWidget {
final LabResult recentLabResult; final LabResult recentLabResult;
@ -63,6 +64,30 @@ class LabResultDetails extends StatelessWidget {
LabNameAndStatus(context), LabNameAndStatus(context),
getLabDescription(context), getLabDescription(context),
LabGraph(context), LabGraph(context),
Container(
padding: EdgeInsets.symmetric(horizontal: 24.h, vertical: 16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.history.tr(context: context).toText16(isBold: true, color: AppColors.textColor),
(labVM.isLabResultsHistoryShowMore ? "Show Less" : "Show More").toText14(weight: FontWeight.w500, color: AppColors.primaryRedColor, isUnderLine: true).onPress(() {
labVM.setIsLabResultsHistoryShowMore();
}),
],
),
SizedBox(height: 24.h,),
labHistoryList(labVM),
],
),
),
Selector<LabViewModel, LabOrderResponseByAi?>( Selector<LabViewModel, LabOrderResponseByAi?>(
selector: (_, model) => model.labOrderResponseByAi, selector: (_, model) => model.labOrderResponseByAi,
builder: (_, aiData, __) { builder: (_, aiData, __) {
@ -170,7 +195,7 @@ class LabResultDetails extends StatelessWidget {
], ],
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
("${LocaleKeys.resultOf.tr(context: context)} ${recentLabResult.verifiedOn ?? ""}").toText11(weight: FontWeight.w500, color: AppColors.greyTextColor), ("${LocaleKeys.resultOf.tr(context: context)} ${recentLabResult.verifiedOn ?? ""}").toText11(weight: FontWeight.w500, color: AppColors.greyTextColor, isEnglishOnly: true),
], ],
), ),
Row( Row(
@ -232,24 +257,25 @@ class LabResultDetails extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( (labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context)).toText16(isBold: true, color: AppColors.textColor),
labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context), // Text(
style: TextStyle( // labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context),
fontSize: 16.f, // style: TextStyle(
fontFamily: 'Poppins', // fontSize: 16.f,
fontWeight: FontWeight.w600, // fontFamily: 'Poppins',
color: AppColors.textColor, // fontWeight: FontWeight.w600,
), // color: AppColors.textColor,
), // ),
// ),
Row( Row(
spacing: 16.h, spacing: 16.h,
children: [ children: [
//todo handle when the graph icon is being displayed //todo handle when the graph icon is being displayed
Utils.buildSvgWithAssets(icon: labmodel.isGraphVisible ? AppAssets.ic_list : AppAssets.ic_graph, width: 24.h, height: 24.h).onPress(() { // Utils.buildSvgWithAssets(icon: labmodel.isGraphVisible ? AppAssets.ic_list : AppAssets.ic_graph, width: 24.h, height: 24.h).onPress(() {
if (labmodel.shouldShowGraph) { // if (labmodel.shouldShowGraph) {
labmodel.alterGraphVisibility(); // labmodel.alterGraphVisibility();
} // }
}), // }),
Utils.buildSvgWithAssets(icon: AppAssets.ic_date_filter, width: 24, height: 24).onPress(() { Utils.buildSvgWithAssets(icon: AppAssets.ic_date_filter, width: 24, height: 24).onPress(() {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: LocaleKeys.setTheDateRange.tr(context: context), title: LocaleKeys.setTheDateRange.tr(context: context),
@ -311,6 +337,7 @@ class LabResultDetails extends StatelessWidget {
minY: labmodel.minY, minY: labmodel.minY,
maxX: labmodel.filteredGraphValues.length.toDouble() - .75, maxX: labmodel.filteredGraphValues.length.toDouble() - .75,
horizontalInterval: .1, horizontalInterval: .1,
isRTL: getIt.get<AppState>().isArabic(),
getDrawingHorizontalLine: (value) { getDrawingHorizontalLine: (value) {
value = double.parse(value.toStringAsFixed(1)); value = double.parse(value.toStringAsFixed(1));
if (value == labmodel.highRefrenceValue || value == labmodel.lowRefenceValue) { if (value == labmodel.highRefrenceValue || value == labmodel.lowRefenceValue) {
@ -392,10 +419,11 @@ class LabResultDetails extends StatelessWidget {
Widget labHistoryList(LabViewModel labmodel) { Widget labHistoryList(LabViewModel labmodel) {
return SizedBox( return SizedBox(
height: labmodel.filteredGraphValues.length < 3 ? labmodel.filteredGraphValues.length * 64 : 180.h, height: labmodel.filteredGraphValues.length < 3 ? labmodel.filteredGraphValues.length * 64 : (!labmodel.isLabResultsHistoryShowMore ? 180.h : 220.h),
child: ListView.separated( child: ListView.separated(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
itemCount: labmodel.filteredGraphValues.length, // itemCount: labmodel.isLabResultsHistoryShowMore ? (labmodel.filteredGraphValues.length < 3 ? labmodel.filteredGraphValues : 3) : labmodel.filteredGraphValues.length,
itemCount: !labmodel.isLabResultsHistoryShowMore ? (labmodel.filteredGraphValues.length < 3 ? labmodel.filteredGraphValues.length : 3) : labmodel.filteredGraphValues.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
var data = labmodel.filteredGraphValues.reversed.toList()[index]; var data = labmodel.filteredGraphValues.reversed.toList()[index];
return LabHistoryItem( return LabHistoryItem(

@ -78,8 +78,9 @@ class _SearchLabResultsContentState extends State<SearchLabResultsContent> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
TextInputWidget( TextInputWidget(
labelText: "Search lab results", fontFamily: "Poppins",
hintText: "Type test name", labelText: LocaleKeys.searchLabResults.tr(context: context),
hintText: "",
controller: searchEditingController, controller: searchEditingController,
isEnable: true, isEnable: true,
prefix: null, prefix: null,
@ -93,7 +94,7 @@ class _SearchLabResultsContentState extends State<SearchLabResultsContent> {
), ),
SizedBox(height: ResponsiveExtension(20).h), SizedBox(height: ResponsiveExtension(20).h),
if (filteredSuggestions.isNotEmpty) ...[ if (filteredSuggestions.isNotEmpty) ...[
"Suggestions".toText16(isBold: true), "Suggestions".toText16(isBold: true, isEnglishOnly: true),
], ],
], ],
), ),
@ -147,6 +148,7 @@ class SuggestionChip extends StatelessWidget {
// color: isSelected ? AppColors.textColor : Colors.black87, // color: isSelected ? AppColors.textColor : Colors.black87,
color: AppColors.textColor, color: AppColors.textColor,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
isEnglishOnly: true
), ),
), ),
); );

@ -546,7 +546,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
doctorImageURL: patientAppointmentHistoryResponseModel.doctorImageURL, doctorImageURL: patientAppointmentHistoryResponseModel.doctorImageURL,
doctorTitle: patientAppointmentHistoryResponseModel.doctorTitle, doctorTitle: patientAppointmentHistoryResponseModel.doctorTitle,
name: patientAppointmentHistoryResponseModel.doctorNameObj, name: patientAppointmentHistoryResponseModel.doctorNameObj,
nationalityFlagURL: "https://hmgwebservices.com/Images/flag/SYR.png", nationalityFlagURL: "",
speciality: [], speciality: [],
clinicName: patientAppointmentHistoryResponseModel.clinicName, clinicName: patientAppointmentHistoryResponseModel.clinicName,
projectName: patientAppointmentHistoryResponseModel.projectName, projectName: patientAppointmentHistoryResponseModel.projectName,
@ -1566,7 +1566,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
label: LocaleKeys.weight.tr(context: context), label: LocaleKeys.weight.tr(context: context),
value: vitalSign.weightKg?.toString() ?? '--', value: vitalSign.weightKg?.toString() ?? '--',
unit: 'kg', unit: 'kg',
status: vitalSign.weightKg != null ? "Normal" : null, status: vitalSign.weightKg != null ? _getWeightStatus(vitalSign.bodyMassIndex) : null,
onTap: onTap, onTap: onTap,
), ),
), ),
@ -1593,6 +1593,11 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
return VitalSignUiModel.bmiStatus(bmi); return VitalSignUiModel.bmiStatus(bmi);
} }
String? _getWeightStatus(dynamic bmi) {
if (bmi == null) return 'Normal';
return VitalSignUiModel.bmiStatus(bmi);
}
String? _getBloodPressureStatus({dynamic systolic, dynamic diastolic}) { String? _getBloodPressureStatus({dynamic systolic, dynamic diastolic}) {
return VitalSignUiModel.bloodPressureStatus(systolic: systolic, diastolic: diastolic); return VitalSignUiModel.bloodPressureStatus(systolic: systolic, diastolic: diastolic);
} }

@ -419,6 +419,7 @@ class _MedicalReportsPageState extends State<MedicalReportsPage> {
callBackFunc: () {}, callBackFunc: () {},
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
isAutoDismiss: true
); );
} }
} }

@ -56,6 +56,7 @@ class _MyInvoicesDetailsPageState extends State<MyInvoicesDetailsPage> {
callBackFunc: () {}, callBackFunc: () {},
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
isAutoDismiss: true
); );
}, },
onError: (err) { onError: (err) {

@ -17,144 +17,114 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
class NotificationsListPage extends StatelessWidget { class NotificationsListPage extends StatefulWidget {
const NotificationsListPage({super.key}); const NotificationsListPage({super.key});
@override
State<NotificationsListPage> createState() => _NotificationsListPageState();
}
class _NotificationsListPageState extends State<NotificationsListPage> {
final GlobalKey _bottomKey = GlobalKey();
void _scrollToBottom() {
WidgetsBinding.instance.addPostFrameCallback((_) {
final ctx = _bottomKey.currentContext;
if (ctx != null) {
Scrollable.ensureVisible(
ctx,
duration: const Duration(milliseconds: 400),
curve: Curves.easeOut,
);
}
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CollapsingListView( return CollapsingListView(
title: LocaleKeys.notification.tr(context: context), title: LocaleKeys.notification.tr(context: context),
child: SingleChildScrollView( child: Consumer<NotificationsViewModel>(builder: (context, notificationsVM, child) {
child: Consumer<NotificationsViewModel>(builder: (context, notificationsVM, child) { return Column(
return Container( children: [
margin: EdgeInsets.symmetric(vertical: 24.h), Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( margin: EdgeInsets.symmetric(vertical: 24.h),
color: AppColors.whiteColor, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
borderRadius: 24.h, color: AppColors.whiteColor,
hasShadow: false, borderRadius: 24.h,
), hasShadow: false,
child: ListView.builder( ),
itemCount: notificationsVM.isNotificationsLoading ? 4 : notificationsVM.notificationsList.length, child: ListView.builder(
physics: NeverScrollableScrollPhysics(), itemCount: notificationsVM.isNotificationsLoading
shrinkWrap: true, ? 4
padding: EdgeInsetsGeometry.zero, : notificationsVM.notificationsList.length + (notificationsVM.hasMoreNotifications ? 1 : 0),
itemBuilder: (context, index) { physics: NeverScrollableScrollPhysics(),
return notificationsVM.isNotificationsLoading shrinkWrap: true,
? LabResultItemView( padding: EdgeInsetsGeometry.zero,
itemBuilder: (context, index) {
if (notificationsVM.isNotificationsLoading) {
return LabResultItemView(
onTap: () {}, onTap: () {},
labOrder: null, labOrder: null,
index: index, index: index,
isLoading: true, isLoading: true,
) );
: AnimationConfiguration.staggeredList( }
position: index, return AnimationConfiguration.staggeredList(
duration: const Duration(milliseconds: 500), position: index,
child: SlideAnimation( duration: const Duration(milliseconds: 500),
verticalOffset: 100.0, child: SlideAnimation(
child: FadeInAnimation( verticalOffset: 100.0,
child: GestureDetector( child: FadeInAnimation(
onTap: () { child: GestureDetector(
Navigator.push( onTap: () {
context, notificationsVM.markAsRead(notificationID: notificationsVM.notificationsList[index].id ?? 0);
MaterialPageRoute( Navigator.push(
builder: (context) => NotificationDetailsPage( context,
notification: notificationsVM.notificationsList[index], MaterialPageRoute(
), builder: (context) => NotificationDetailsPage(
), notification: notificationsVM.notificationsList[index],
);
},
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
// Message row with red dot for unread
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: notificationsVM.notificationsList[index].message!.toText16(
isBold: (notificationsVM.notificationsList[index].isRead == false),
weight: (notificationsVM.notificationsList[index].isRead == false)
? FontWeight.w600
: FontWeight.w400,
),
), ),
SizedBox(width: 8.w), ),
// Red dot for unread notifications ONLY );
if (notificationsVM.notificationsList[index].isRead == false) },
Container( child: AnimatedContainer(
width: 8.w, duration: Duration(milliseconds: 300),
height: 8.w, curve: Curves.easeInOut,
decoration: BoxDecoration( child: Column(
color: Colors.red, crossAxisAlignment: CrossAxisAlignment.start,
shape: BoxShape.circle,
),
),
],
),
SizedBox(height: 12.h),
// First row: Time and Date chips with arrow
Row(
children: [ children: [
// Time chip with clock icon SizedBox(height: 16.h),
Container( // Message row with red dot for unread
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), Row(
decoration: BoxDecoration( crossAxisAlignment: CrossAxisAlignment.start,
color: AppColors.greyColor, children: [
borderRadius: BorderRadius.circular(8), Expanded(
), child: notificationsVM.notificationsList[index].message!.toText16(
child: Row( isBold: (notificationsVM.notificationsList[index].isRead == false),
mainAxisSize: MainAxisSize.min, weight: (notificationsVM.notificationsList[index].isRead == false)
children: [ ? FontWeight.w600
Icon(Icons.access_time, size: 12.w, color: AppColors.textColor), : FontWeight.w400,
SizedBox(width: 4.w),
_formatTime(notificationsVM.notificationsList[index].isSentOn).toText10(
weight: FontWeight.w500,
color: AppColors.textColor,
), ),
], ),
), SizedBox(width: 8.w),
), // Red dot for unread notifications ONLY
SizedBox(width: 8.w), if (notificationsVM.notificationsList[index].isRead == false)
// Date chip with calendar icon Container(
Container( width: 8.w,
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), height: 8.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.greyColor, color: Colors.red,
borderRadius: BorderRadius.circular(8), shape: BoxShape.circle,
), ),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.calendar_today, size: 12.w, color: AppColors.textColor),
SizedBox(width: 4.w),
_formatDate(notificationsVM.notificationsList[index].isSentOn).toText10(
weight: FontWeight.w500,
color: AppColors.textColor,
), ),
], ],
),
),
Spacer(),
// Arrow icon
Utils.buildSvgWithAssets(
icon: AppAssets.arrow_forward,
width: 16.w,
height: 16.h,
iconColor: AppColors.greyTextColor,
), ),
], SizedBox(height: 12.h),
), // First row: Time and Date chips with arrow
// Second row: Contains Image chip (if MessageType is "image") Row(
if (notificationsVM.notificationsList[index].messageType != null &&
notificationsVM.notificationsList[index].messageType!.toLowerCase() == "image")
Padding(
padding: EdgeInsets.only(top: 8.h),
child: Row(
children: [ children: [
// Time chip with clock icon
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -164,31 +134,100 @@ class NotificationsListPage extends StatelessWidget {
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon(Icons.image_outlined, size: 12.w, color: AppColors.textColor), Icon(Icons.access_time, size: 12.w, color: AppColors.textColor),
SizedBox(width: 4.w), SizedBox(width: 4.w),
'Contains Image'.toText10( _formatTime(notificationsVM.notificationsList[index].isSentOn).toText10(
weight: FontWeight.w500, weight: FontWeight.w500,
color: AppColors.textColor, color: AppColors.textColor,
), ),
], ],
), ),
), ),
SizedBox(width: 8.w),
// Date chip with calendar icon
Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration(
color: AppColors.greyColor,
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.calendar_today, size: 12.w, color: AppColors.textColor),
SizedBox(width: 4.w),
_formatDate(notificationsVM.notificationsList[index].isSentOn).toText10(
weight: FontWeight.w500,
color: AppColors.textColor,
),
],
),
),
Spacer(),
// Arrow icon
Utils.buildSvgWithAssets(
icon: AppAssets.arrow_forward,
width: 16.w,
height: 16.h,
iconColor: AppColors.greyTextColor,
),
], ],
), ),
), // Second row: Contains Image chip (if MessageType is "image")
SizedBox(height: 16.h), if (notificationsVM.notificationsList[index].messageType != null &&
1.divider, notificationsVM.notificationsList[index].messageType!.toLowerCase() == "image")
], Padding(
padding: EdgeInsets.only(top: 8.h),
child: Row(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration(
color: AppColors.greyColor,
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.image_outlined, size: 12.w, color: AppColors.textColor),
SizedBox(width: 4.w),
'Contains Image'.toText10(
weight: FontWeight.w500,
color: AppColors.textColor,
),
],
),
),
],
),
),
SizedBox(height: 16.h),
1.divider,
],
),
),
), ),
), ),
), ),
), );
), }).paddingSymmetrical(16.w, 0.h),
); ).paddingSymmetrical(24.w, 0.h),
}).paddingSymmetrical(16.w, 0.h), SizedBox(height: 16.h),
).paddingSymmetrical(24.w, 0.h); SizedBox(
key: _bottomKey,
child: "Show more notifications".toText16(
color: AppColors.primaryRedColor,
isBold: true,
isUnderLine: true
),
).onPress(() async {
await notificationsVM.loadMoreNotifications();
_scrollToBottom();
}),
SizedBox(height: 24.h),
],
);
}), }),
),
); );
} }

@ -1,7 +1,10 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_export.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/extensions/string_extensions.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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
@ -103,6 +106,18 @@ class _ParkingPageState extends State<ParkingPage> {
child: Padding( child: Padding(
padding: EdgeInsets.all(16.h), child: LocaleKeys.parkingDescription.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.textColor)), padding: EdgeInsets.all(16.h), child: LocaleKeys.parkingDescription.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.textColor)),
).paddingOnly(top: 16, bottom: 16), ).paddingOnly(top: 16, bottom: 16),
ClipRRect(
borderRadius: BorderRadius.circular(24.r),
child: Transform.flip(
flipX: getIt.get<AppState>().isArabic(),
child: Image.asset(
AppAssets.carParkingService,
fit: BoxFit.fitHeight,
height: 480.h,
width: 520.w,
),
),
),
], ],
), ),
), ),

@ -135,6 +135,7 @@ class PrescriptionDeliveryOrderSummaryPage extends StatelessWidget {
isCloseButtonVisible: true, isCloseButtonVisible: true,
isDismissible: false, isDismissible: false,
isFullScreen: false, isFullScreen: false,
isAutoDismiss: true
); );
}, },
onError: (err) { onError: (err) {

@ -72,7 +72,7 @@ class _PreferredLanguageWidgetState extends State<PreferredLanguageWidget> {
callBackFunc: () async { callBackFunc: () async {
Navigator.of(context).pop(); Navigator.of(context).pop();
profileSettingsViewModel.getProfileSettings(); profileSettingsViewModel.getProfileSettings();
}, isFullScreen: false); }, isFullScreen: false, isAutoDismiss: true);
}, },
onError: (error) { onError: (error) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();

@ -96,7 +96,7 @@ class _UpdateEmailDialogState extends State<UpdateEmailDialog> {
callBackFunc: () async { callBackFunc: () async {
Navigator.of(context).pop(); Navigator.of(context).pop();
profileSettingsViewModel!.getProfileSettings(); profileSettingsViewModel!.getProfileSettings();
}, isFullScreen: false); }, isFullScreen: false, isAutoDismiss: true);
}, },
onError: (error) { onError: (error) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();

@ -96,7 +96,7 @@ class _UpdateEmergencyContactDialogState extends State<UpdateEmergencyContactDia
callBackFunc: () async { callBackFunc: () async {
Navigator.of(context).pop(); Navigator.of(context).pop();
profileSettingsViewModel!.getProfileSettings(); profileSettingsViewModel!.getProfileSettings();
}, isFullScreen: false); }, isFullScreen: false, isAutoDismiss: true);
}, },
onError: (error) { onError: (error) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();

@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/presentation/rate_appointment/rate_appointme
import 'package:hmg_patient_app_new/presentation/rate_appointment/widget/doctor_row.dart'; import 'package:hmg_patient_app_new/presentation/rate_appointment/widget/doctor_row.dart';
import 'package:hmg_patient_app_new/theme/colors.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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class RateAppointmentDoctor extends StatefulWidget { class RateAppointmentDoctor extends StatefulWidget {
@ -25,7 +26,7 @@ class RateAppointmentDoctor extends StatefulWidget {
class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> { class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
final formKey = GlobalKey<FormState>(); final formKey = GlobalKey<FormState>();
String note = ""; String note = "";
int rating = 5; int rating = 0;
// ProjectViewModel? projectViewModel; // ProjectViewModel? projectViewModel;
AppointmentRatingViewModel? appointmentRatingViewModel; AppointmentRatingViewModel? appointmentRatingViewModel;
@ -48,16 +49,16 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
return Selector<AppointmentRatingViewModel, bool>( return Selector<AppointmentRatingViewModel, bool>(
selector: (_, vm) => vm.isRateClinic, selector: (_, vm) => vm.isRateClinic,
builder: (context, isRateClinic, child) => isRateClinic builder: (context, isRateClinic, child) =>
? RateAppointmentClinic(doctorNote: note, doctorRate: rating,) // isRateClinic ? RateAppointmentClinic(doctorNote: note, doctorRate: rating,)
: SizedBox( // :
height: sheetHeight, SizedBox(
height: sheetHeight,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Scrollable main content // Scrollable main content
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 0.0, left: 0, right: 0), padding: const EdgeInsets.only(top: 0.0, left: 0, right: 0),
child: Column( child: Column(
@ -144,7 +145,7 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
decoration: InputDecoration.collapsed( decoration: InputDecoration.collapsed(
hintText: LocaleKeys.notes.tr(context: context), hintText: LocaleKeys.notes.tr(context: context),
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 16, fontSize: 16.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xff2B353E), color: Color(0xff2B353E),
letterSpacing: -0.64, letterSpacing: -0.64,
@ -162,7 +163,6 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
), ),
), ),
// Bottom action buttons pinned to bottom // Bottom action buttons pinned to bottom
SafeArea( SafeArea(
top: false, top: false,
@ -172,7 +172,7 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
children: [ children: [
Expanded( Expanded(
child: CustomButton( child: CustomButton(
text: "Later", text: LocaleKeys.later.tr(context: context),
backgroundColor: Color(0xffFEE9EA), backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B), textColor: Color(0xffED1C2B),
@ -184,12 +184,18 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
SizedBox(width: 10), SizedBox(width: 10),
Expanded( Expanded(
child: CustomButton( child: CustomButton(
text: LocaleKeys.next.tr(context: context), text: LocaleKeys.submit.tr(context: context),
onPressed: () { onPressed: () async {
// Set up clinic rating and show clinic rating view // Set up clinic rating and show clinic rating view
appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),); // appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),);
appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),); // appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),);
appointmentRatingViewModel!.setClinicOrDoctor(true); // appointmentRatingViewModel!.setClinicOrDoctor(true);
LoaderBottomSheet.showLoader();
await appointmentRatingViewModel!.submitDoctorRating(docRate: rating, docNote: note);
// await appointmentRatingViewModel!.submitClinicRating(clinicRate: rating, clinicNote: note);
LoaderBottomSheet.hideLoader();
Navigator.pop(context);
setState(() {}); setState(() {});
}, },

@ -36,32 +36,26 @@ class BuildDoctorRow extends StatelessWidget {
SizedBox(height: 8.h), SizedBox(height: 8.h),
isForClinic ? Wrap( isForClinic ? Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
spacing: 3.h, spacing: 3.h,
runSpacing: 4.h, runSpacing: 4.h,
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
labelText: labelText:
appointmentDetails!.clinicName.toString(), appointmentDetails!.clinicName.toString(),
), ),
AppCustomChipWidget( AppCustomChipWidget(
icon: AppAssets.ic_date_filter, icon: AppAssets.ic_date_filter,
labelText: labelText:
DateUtil.formatDateToDate(DateUtil.convertStringToDate(appointmentDetails!.appointmentDate), false), DateUtil.formatDateToDate(DateUtil.convertStringToDate(appointmentDetails!.appointmentDate), false),
), ),
AppCustomChipWidget( AppCustomChipWidget(
icon: AppAssets.appointment_time_icon, icon: AppAssets.appointment_time_icon,
labelText: labelText:
appointmentDetails!.startTime.substring(0, appointmentDetails!.startTime.length - 3), appointmentDetails!.startTime.substring(0, appointmentDetails!.startTime.length - 3),
), ),
] ]
) : Wrap( ) : Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
@ -69,26 +63,26 @@ class BuildDoctorRow extends StatelessWidget {
runSpacing: 4.h, runSpacing: 4.h,
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
labelText: labelText:
appointmentDetails!.projectName.toString(), appointmentDetails!.projectName.toString(),
), ),
AppCustomChipWidget( AppCustomChipWidget(
labelText: labelText:
appointmentDetails!.clinicName.toString(), appointmentDetails!.clinicName.toString(),
),
) AppCustomChipWidget(
labelPadding: EdgeInsetsDirectional.only(start: -8.w, end: 6.w),
] icon: AppAssets.ic_date_filter,
) labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(appointmentDetails!.appointmentDate), false),
], ),
],
),
],
),
), ),
),
], ],
)); ),
);
} }
} }

@ -37,6 +37,8 @@ class PossibleConditionsPage extends StatelessWidget {
late BookAppointmentsViewModel bookAppointmentsViewModel; late BookAppointmentsViewModel bookAppointmentsViewModel;
late AppointmentViaRegionViewmodel regionalViewModel; late AppointmentViaRegionViewmodel regionalViewModel;
late AppState appState;
Widget _buildLoadingShimmer() { Widget _buildLoadingShimmer() {
return ListView.separated( return ListView.separated(
shrinkWrap: true, shrinkWrap: true,
@ -223,6 +225,7 @@ class PossibleConditionsPage extends StatelessWidget {
symptomsCheckerViewModel = context.read<SymptomsCheckerViewModel>(); symptomsCheckerViewModel = context.read<SymptomsCheckerViewModel>();
bookAppointmentsViewModel = context.read<BookAppointmentsViewModel>(); bookAppointmentsViewModel = context.read<BookAppointmentsViewModel>();
regionalViewModel = context.read<AppointmentViaRegionViewmodel>(); regionalViewModel = context.read<AppointmentViaRegionViewmodel>();
appState = getIt.get<AppState>();
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView( body: CollapsingListView(
@ -248,7 +251,7 @@ class PossibleConditionsPage extends StatelessWidget {
// Get selected symptoms names for display // Get selected symptoms names for display
final symptoms = symptomsCheckerViewModel final symptoms = symptomsCheckerViewModel
.getAllSelectedSymptoms() .getAllSelectedSymptoms()
.map((s) => s.commonName ?? s.name ?? '') .map((s) => (appState.isArabic() ? s.commonNameAr ?? s.nameAr ?? '' : s.commonName ?? s.name ?? ''))
.where((name) => name.isNotEmpty) .where((name) => name.isNotEmpty)
.take(3) .take(3)
.toList(); .toList();
@ -376,7 +379,6 @@ class PossibleConditionsPage extends StatelessWidget {
}, },
); );
} else { } else {
final appState = getIt.get<AppState>();
appState.resetLocation(); appState.resetLocation();
regionVM.setSortByLocation(false); regionVM.setSortByLocation(false);
_refreshHospitalListAfterApi(regionVM); _refreshHospitalListAfterApi(regionVM);

@ -491,7 +491,7 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
child: Utils.getSuccessWidget(loadingText: "${LocaleKeys.paymentCompletedSuccessfully.tr(context: context)}, ${LocaleKeys.hereIsYourInvoiceNumber.tr(context: context)}$invoiceNo"), child: Utils.getSuccessWidget(loadingText: "${LocaleKeys.paymentCompletedSuccessfully.tr(context: context)}, ${LocaleKeys.hereIsYourInvoiceNumber.tr(context: context)}$invoiceNo"),
isCloseButtonVisible: true, isCloseButtonVisible: true,
isDismissible: true, isDismissible: true,
isFullScreen: false, callBackFunc: () { isFullScreen: false, isAutoDismiss: true, callBackFunc: () {
// Pop until we reach the LandingPage // Pop until we reach the LandingPage
todoSectionViewModel.setIsAncillaryOrdersNeedReloading(true); todoSectionViewModel.setIsAncillaryOrdersNeedReloading(true);
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(

@ -89,6 +89,21 @@ class _VitalSignDetailsPageState extends State<VitalSignDetailsPage> {
), ),
_whatIsThisResultCard(context), _whatIsThisResultCard(context),
_historyCard(context, history: history), _historyCard(context, history: history),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.r,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.history.tr(context: context).toText16(isBold: true, color: AppColors.textColor),
SizedBox(height: 16.h),
_buildHistoryList(context, history),
],
).paddingSymmetrical(16.h, 16.h),
),
], ],
).paddingAll(24.h), ).paddingAll(24.h),
); );
@ -264,35 +279,27 @@ class _VitalSignDetailsPageState extends State<VitalSignDetailsPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( LocaleKeys.historyFlowchart.tr(context: context).toText16(isBold: true, color: AppColors.textColor),
_isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context),
style: TextStyle(
fontSize: 16,
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
color: AppColors.textColor,
),
),
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Container( // Container(
width: 24.h, // width: 24.h,
height: 24.h, // height: 24.h,
alignment: Alignment.center, // alignment: Alignment.center,
child: InkWell( // child: InkWell(
onTap: () { // onTap: () {
setState(() { // setState(() {
_isGraphVisible = !_isGraphVisible; // _isGraphVisible = !_isGraphVisible;
}); // });
}, // },
child: Utils.buildSvgWithAssets( // child: Utils.buildSvgWithAssets(
icon: _isGraphVisible ? AppAssets.ic_list : AppAssets.ic_graph, // icon: _isGraphVisible ? AppAssets.ic_list : AppAssets.ic_graph,
width: 24.h, // width: 24.h,
height: 24.h, // height: 24.h,
), // ),
), // ),
), // ),
// SizedBox(width: 16.h), // SizedBox(width: 16.h),
// Container( // Container(
// width: 24.h, // width: 24.h,
@ -663,7 +670,8 @@ class _VitalSignDetailsPageState extends State<VitalSignDetailsPage> {
case VitalSignMetric.height: case VitalSignMetric.height:
return null; return null;
case VitalSignMetric.weight: case VitalSignMetric.weight:
return latest.weightKg != null ? 'Normal' : null; // return latest.weightKg != null ? 'Normal' : null;
return VitalSignUiModel.bmiStatus(latest.bodyMassIndex);
case VitalSignMetric.temperature: case VitalSignMetric.temperature:
return null; return null;
case VitalSignMetric.heartRate: case VitalSignMetric.heartRate:

@ -138,11 +138,13 @@ class _VitalSignPageState extends State<VitalSignPage> {
label: LocaleKeys.weight.tr(context: context), label: LocaleKeys.weight.tr(context: context),
value: latestVitalSign?.weightKg?.toString() ?? '--', value: latestVitalSign?.weightKg?.toString() ?? '--',
unit: 'kg', unit: 'kg',
status: (latestVitalSign?.weightKg != null) ? 'Normal' : null, status: (latestVitalSign?.weightKg != null)
? VitalSignUiModel.bmiStatus(latestVitalSign?.bodyMassIndex)
: null,
onTap: () => _openDetails( onTap: () => _openDetails(
VitalSignDetailsArgs( VitalSignDetailsArgs(
metric: VitalSignMetric.weight, metric: VitalSignMetric.weight,
title: LocaleKeys.height.tr(context: context), title: LocaleKeys.weight.tr(context: context),
icon: AppAssets.weightVital, icon: AppAssets.weightVital,
unit: 'kg', unit: 'kg',
), ),

@ -23,7 +23,7 @@ abstract class DialogService {
Future<void> showExceptionBottomSheet({required String message, required Function() onOkPressed, Function()? onCancelPressed}); Future<void> showExceptionBottomSheet({required String message, required Function() onOkPressed, Function()? onCancelPressed});
Future<void> showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, required Function() onOkPressed, Function()? onCancelPressed}); Future<void> showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed});
Future<void> showSuccessBottomSheetWithoutH({String? label, required String message, required Function() onOkPressed, Function()? onCancelPressed}); Future<void> showSuccessBottomSheetWithoutH({String? label, required String message, required Function() onOkPressed, Function()? onCancelPressed});
@ -119,7 +119,7 @@ class DialogServiceImp implements DialogService {
} }
@override @override
Future<void> showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, required Function() onOkPressed, Function()? onCancelPressed}) async { Future<void> showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed}) async {
final context = navigationService.navigatorKey.currentContext; final context = navigationService.navigatorKey.currentContext;
if (context == null) return; if (context == null) return;
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
@ -132,6 +132,7 @@ class DialogServiceImp implements DialogService {
cancelLabel: cancelLabel, cancelLabel: cancelLabel,
onOkPressed: onOkPressed, onOkPressed: onOkPressed,
onCancelPressed: onCancelPressed, onCancelPressed: onCancelPressed,
isConfirmButton: isConfirmButton
), ),
callBackFunc: () {}, callBackFunc: () {},
); );
@ -239,7 +240,7 @@ class DialogServiceImp implements DialogService {
} }
} }
Widget exceptionBottomSheetWidget({required BuildContext context, required String message, String? okLabel, String? cancelLabel, required Function() onOkPressed, Function()? onCancelPressed}) { Widget exceptionBottomSheetWidget({required BuildContext context, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed}) {
return Column( return Column(
children: [ children: [
(message).toText16(isBold: false, color: AppColors.textColor), (message).toText16(isBold: false, color: AppColors.textColor),
@ -267,7 +268,7 @@ Widget exceptionBottomSheetWidget({required BuildContext context, required Strin
text: okLabel ?? LocaleKeys.confirm.tr(context: context), text: okLabel ?? LocaleKeys.confirm.tr(context: context),
onPressed: onOkPressed, onPressed: onOkPressed,
backgroundColor: AppColors.bgGreenColor, backgroundColor: AppColors.bgGreenColor,
borderColor: AppColors.bgGreenColor, borderColor: AppColors.successColor,
textColor: Colors.white, textColor: Colors.white,
icon: AppAssets.confirm, icon: AppAssets.confirm,
), ),
@ -284,8 +285,9 @@ Widget exceptionBottomSheetWidget({required BuildContext context, required Strin
context.pop(); context.pop();
} }
: onCancelPressed, : onCancelPressed,
backgroundColor: AppColors.primaryRedColor, // backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedBorderColor, backgroundColor: isConfirmButton ? AppColors.successColor : AppColors.primaryRedColor,
borderColor: isConfirmButton ? AppColors.successColor : AppColors.primaryRedColor,
textColor: Colors.white, textColor: Colors.white,
icon: AppAssets.confirm, icon: AppAssets.confirm,
), ),

@ -57,9 +57,9 @@ class _AppLanguageChangeState extends State<AppLanguageChange> {
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: Column( child: Column(
children: [ children: [
languageItem("English", "en"),
1.divider,
languageItem("العربية", "ar"), languageItem("العربية", "ar"),
1.divider,
languageItem("English", "en"),
], ],
), ),
), ),

@ -116,6 +116,7 @@ void showCommonBottomSheet(BuildContext context,
Function(String?)? callBackFunc, Function(String?)? callBackFunc,
String? title, String? title,
required double height, required double height,
bool isAutoDismiss = false,
bool isCloseButtonVisible = true, bool isCloseButtonVisible = true,
bool isFullScreen = true, bool isFullScreen = true,
bool isDismissible = true, bool isDismissible = true,
@ -131,6 +132,14 @@ void showCommonBottomSheet(BuildContext context,
isDismissible: isDismissible, isDismissible: isDismissible,
backgroundColor: isSuccessDialog ? AppColors.whiteColor : AppColors.scaffoldBgColor, backgroundColor: isSuccessDialog ? AppColors.whiteColor : AppColors.scaffoldBgColor,
builder: (BuildContext context) { builder: (BuildContext context) {
if (isAutoDismiss) {
Future.delayed(Duration(seconds: 2), () {
Navigator.of(context).pop();
if (callBackFunc != null) {
callBackFunc(null);
}
});
}
return Container( return Container(
height: height, height: height,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.scaffoldBgColor, borderRadius: 24.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.scaffoldBgColor, borderRadius: 24.h),
@ -211,9 +220,11 @@ void showCommonBottomSheetWithoutHeight(
required Widget child, required Widget child,
VoidCallback? callBackFunc, VoidCallback? callBackFunc,
String title = "", String title = "",
bool isConfirmButton = false,
bool isCloseButtonVisible = true, bool isCloseButtonVisible = true,
bool isFullScreen = true, bool isFullScreen = true,
bool isDismissible = true, bool isDismissible = true,
bool isAutoDismiss = false,
Widget? titleWidget, Widget? titleWidget,
bool useSafeArea = false, bool useSafeArea = false,
bool hasBottomPadding = true, bool hasBottomPadding = true,
@ -237,6 +248,14 @@ void showCommonBottomSheetWithoutHeight(
backgroundColor: resolvedBgColor, backgroundColor: resolvedBgColor,
useSafeArea: useSafeArea, useSafeArea: useSafeArea,
builder: (BuildContext context) { builder: (BuildContext context) {
if (isAutoDismiss) {
Future.delayed(Duration(seconds: 2), () {
Navigator.of(context).pop();
if (callBackFunc != null) {
callBackFunc();
}
});
}
return SafeArea( return SafeArea(
top: false, top: false,
left: false, left: false,

@ -48,6 +48,14 @@ class CustomTabBarState extends State<CustomTabBar> {
super.initState(); super.initState();
} }
@override
void didUpdateWidget(covariant CustomTabBar oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.initialIndex != widget.initialIndex) {
selectedIndex = widget.initialIndex;
}
}
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();

@ -31,6 +31,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
/// - [showBottomTitleDates]: Whether to show bottom axis labels. /// - [showBottomTitleDates]: Whether to show bottom axis labels.
/// - [isFullScreeGraph]: Whether the graph is fullscreen. /// - [isFullScreeGraph]: Whether the graph is fullscreen.
/// - [makeGraphBasedOnActualValue]: Use `actualValue` for plotting. /// - [makeGraphBasedOnActualValue]: Use `actualValue` for plotting.
/// - [isRTL]: Whether the graph should render in right-to-left direction.
/// ///
/// Example usage: /// Example usage:
/// ```dart /// ```dart
@ -70,6 +71,7 @@ class CustomGraph extends StatelessWidget {
final bool showLinePoints; final bool showLinePoints;
final double? cutOffY; final double? cutOffY;
final RangeAnnotations? rangeAnnotations; final RangeAnnotations? rangeAnnotations;
final bool isRTL;
///creates the left label and provide it to the chart as it will be used by other part of the application so the label will be different for every chart ///creates the left label and provide it to the chart as it will be used by other part of the application so the label will be different for every chart
final Widget Function(double) leftLabelFormatter; final Widget Function(double) leftLabelFormatter;
@ -113,7 +115,8 @@ class CustomGraph extends StatelessWidget {
this.showShadow = false, this.showShadow = false,
this.showLinePoints = false, this.showLinePoints = false,
this.cutOffY = 0, this.cutOffY = 0,
this.rangeAnnotations}); this.rangeAnnotations,
this.isRTL = false});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -122,6 +125,12 @@ class CustomGraph extends StatelessWidget {
final resolvedGraphShadowColor = graphShadowColor ?? AppColors.graphGridColor; final resolvedGraphShadowColor = graphShadowColor ?? AppColors.graphGridColor;
final resolvedGraphGridColor = graphGridColor ?? AppColors.graphGridColor; final resolvedGraphGridColor = graphGridColor ?? AppColors.graphGridColor;
// For RTL, reverse the data points so the graph reads right-to-left
final displayDataPoints = isRTL ? dataPoints.reversed.toList() : dataPoints;
final displaySecondaryDataPoints = (isRTL && secondaryDataPoints != null)
? secondaryDataPoints!.reversed.toList()
: secondaryDataPoints;
return Material( return Material(
color: AppColors.whiteColor, color: AppColors.whiteColor,
child: SizedBox( child: SizedBox(
@ -176,10 +185,10 @@ class CustomGraph extends StatelessWidget {
// Show tooltip for each touched line // Show tooltip for each touched line
return touchedSpots.map((spot) { return touchedSpots.map((spot) {
// Determine which dataset this spot belongs to // Determine which dataset this spot belongs to
final isSecondary = secondaryDataPoints != null && spot.barIndex == 1; final isSecondary = displaySecondaryDataPoints != null && spot.barIndex == 1;
final dataPoint = isSecondary final dataPoint = isSecondary
? secondaryDataPoints![spot.x.toInt()] ? displaySecondaryDataPoints[spot.x.toInt()]
: dataPoints[spot.x.toInt()]; : displayDataPoints[spot.x.toInt()];
return LineTooltipItem( return LineTooltipItem(
'${dataPoint.actualValue} ${dataPoint.unitOfMeasurement ?? ""} - ${dataPoint.displayTime}', '${dataPoint.actualValue} ${dataPoint.unitOfMeasurement ?? ""} - ${dataPoint.displayTime}',
@ -192,9 +201,19 @@ class CustomGraph extends StatelessWidget {
titlesData: FlTitlesData( titlesData: FlTitlesData(
leftTitles: AxisTitles( leftTitles: AxisTitles(
sideTitles: SideTitles( sideTitles: SideTitles(
showTitles: true, showTitles: !isRTL,
reservedSize: leftLabelReservedSize ?? 80,
interval: leftLabelInterval ?? .1,
getTitlesWidget: (value, _) {
return leftLabelFormatter(value);
},
),
),
rightTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: isRTL,
reservedSize: leftLabelReservedSize ?? 80, reservedSize: leftLabelReservedSize ?? 80,
interval: leftLabelInterval ?? .1, // Let fl_chart handle it interval: leftLabelInterval ?? .1,
getTitlesWidget: (value, _) { getTitlesWidget: (value, _) {
return leftLabelFormatter(value); return leftLabelFormatter(value);
}, },
@ -206,30 +225,28 @@ class CustomGraph extends StatelessWidget {
showTitles: showBottomTitleDates, showTitles: showBottomTitleDates,
reservedSize: bottomLabelReservedSize ?? 20, reservedSize: bottomLabelReservedSize ?? 20,
getTitlesWidget: (value, _) { getTitlesWidget: (value, _) {
return bottomLabelFormatter(value, dataPoints); return bottomLabelFormatter(value, displayDataPoints);
}, },
interval: 1, // ensures 1:1 mapping with spots interval: 1, // ensures 1:1 mapping with spots
), ),
), ),
topTitles: AxisTitles(), topTitles: AxisTitles(),
rightTitles: AxisTitles(),
), ),
borderData: FlBorderData( borderData: FlBorderData(
show: true, show: true,
border: const Border( border: Border(
bottom: BorderSide.none, bottom: BorderSide.none,
left: BorderSide(color: Colors.grey, width: .5), left: isRTL ? BorderSide.none : const BorderSide(color: Colors.grey, width: .5),
right: BorderSide.none, right: isRTL ? const BorderSide(color: Colors.grey, width: .5) : BorderSide.none,
top: BorderSide.none, top: BorderSide.none,
), ),
), ),
lineBarsData: _buildColoredLineSegments(dataPoints, showLinePoints, resolvedGraphColor, resolvedGraphShadowColor), lineBarsData: _buildColoredLineSegments(displayDataPoints, showLinePoints, resolvedGraphColor, resolvedGraphShadowColor,
secondaryPoints: displaySecondaryDataPoints),
gridData: FlGridData( gridData: FlGridData(
show: showGridLines ?? true, show: showGridLines ?? true,
drawVerticalLine: false, drawVerticalLine: false,
horizontalInterval: horizontalInterval, horizontalInterval: horizontalInterval,
// checkToShowHorizontalLine: (value) =>
// value >= 0 && value <= 100,
getDrawingHorizontalLine: getDrawingHorizontalLine ?? getDrawingHorizontalLine: getDrawingHorizontalLine ??
(value) { (value) {
return FlLine( return FlLine(
@ -245,7 +262,8 @@ class CustomGraph extends StatelessWidget {
); );
} }
List<LineChartBarData> _buildColoredLineSegments(List<DataPoint> dataPoints, bool showLinePoints, Color resolvedGraphColor, Color resolvedGraphShadowColor) { List<LineChartBarData> _buildColoredLineSegments(List<DataPoint> dataPoints, bool showLinePoints, Color resolvedGraphColor, Color resolvedGraphShadowColor,
{List<DataPoint>? secondaryPoints}) {
final List<FlSpot> allSpots = dataPoints.asMap().entries.map((entry) { final List<FlSpot> allSpots = dataPoints.asMap().entries.map((entry) {
double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value; double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value;
return FlSpot(entry.key.toDouble(), value); return FlSpot(entry.key.toDouble(), value);
@ -281,8 +299,8 @@ class CustomGraph extends StatelessWidget {
]; ];
// Add secondary line if provided (for dual-line graphs like blood pressure) // Add secondary line if provided (for dual-line graphs like blood pressure)
if (secondaryDataPoints != null && secondaryDataPoints!.isNotEmpty) { if (secondaryPoints != null && secondaryPoints.isNotEmpty) {
final List<FlSpot> secondarySpots = secondaryDataPoints!.asMap().entries.map((entry) { final List<FlSpot> secondarySpots = secondaryPoints.asMap().entries.map((entry) {
double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value; double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value;
return FlSpot(entry.key.toDouble(), value); return FlSpot(entry.key.toDouble(), value);
}).toList(); }).toList();

@ -289,15 +289,7 @@ class TextInputWidget extends StatelessWidget {
} }
Widget _buildLabelText(Color? labelColor) { Widget _buildLabelText(Color? labelColor) {
return Text( return labelText.toText12(fontWeight: FontWeight.w500, color: labelColor ?? AppColors.inputLabelTextColor);
labelText,
style: TextStyle(
fontSize: 12.f,
fontWeight: FontWeight.w500,
color: labelColor ?? AppColors.inputLabelTextColor,
letterSpacing: -0,
),
);
} }
Widget _buildTextField(BuildContext context) { Widget _buildTextField(BuildContext context) {

@ -5,6 +5,8 @@ import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/route_extensions.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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -13,7 +15,7 @@ class LoaderBottomSheet {
static final NavigationService _navService = GetIt.I<NavigationService>(); static final NavigationService _navService = GetIt.I<NavigationService>();
static bool _isVisible = false; static bool _isVisible = false;
static void showLoader({String? loadingText}) { static void showLoader({String? loadingText, bool showCloseButton = false, Function? onCloseTap}) {
if (_isVisible) return; if (_isVisible) return;
_isVisible = true; _isVisible = true;
@ -32,7 +34,31 @@ class LoaderBottomSheet {
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)), borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
), ),
child: Center( child: (showCloseButton) ? Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"".toText20(),
if (showCloseButton)
IconButton(
icon: Icon(Icons.close, color: AppColors.textColor),
onPressed: () {
context.pop();
if(onCloseTap != null) {
onCloseTap();
}
},
)
else
SizedBox(width: 48), // Placeholder for alignment
],
),
Center(
child: Utils.getLoadingWidget(loadingText: loadingText),
).paddingSymmetrical(24.w, 0),
],
) : Center(
child: Utils.getLoadingWidget(loadingText: loadingText), child: Utils.getLoadingWidget(loadingText: loadingText),
).paddingSymmetrical(24.w, 0), ).paddingSymmetrical(24.w, 0),
); );

@ -10,7 +10,6 @@ class CustomPageRoute<T> extends PageRouteBuilder<T> {
CustomPageRoute({required this.page, this.direction = AxisDirection.right, this.fullScreenDialog = false, super.settings}) CustomPageRoute({required this.page, this.direction = AxisDirection.right, this.fullScreenDialog = false, super.settings})
: super( : super(
transitionDuration: const Duration(milliseconds: 1500), transitionDuration: const Duration(milliseconds: 1500),
reverseTransitionDuration: const Duration(milliseconds: 500), reverseTransitionDuration: const Duration(milliseconds: 500),
fullscreenDialog: fullScreenDialog, fullscreenDialog: fullScreenDialog,

Loading…
Cancel
Save