diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 347ab714..e852236b 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -34,7 +34,8 @@ android { defaultConfig { applicationId = "com.cloudsolutions.HMGPatientApp" // minSdk = 24 - minSdk = 26 +// minSdk = 26 + minSdk = 29 targetSdk = 35 compileSdk = 36 // targetSdk = flutter.targetSdkVersion diff --git a/assets/images/png/Carparking_services_image.png b/assets/images/png/Carparking_services_image.png new file mode 100644 index 00000000..9ce08c78 Binary files /dev/null and b/assets/images/png/Carparking_services_image.png differ diff --git a/assets/images/png/ereferral_services_image.png b/assets/images/png/ereferral_services_image.png index 15194b59..10d0d14a 100644 Binary files a/assets/images/png/ereferral_services_image.png and b/assets/images/png/ereferral_services_image.png differ diff --git a/assets/images/png/healthtrackers_services_image.png b/assets/images/png/healthtrackers_services_image.png new file mode 100644 index 00000000..2d0c3f8a Binary files /dev/null and b/assets/images/png/healthtrackers_services_image.png differ diff --git a/assets/images/png/livechat_services_image.png b/assets/images/png/livechat_services_image.png new file mode 100644 index 00000000..7e33a933 Binary files /dev/null and b/assets/images/png/livechat_services_image.png differ diff --git a/assets/images/png/smartwatch_services_image.png b/assets/images/png/smartwatch_services_image.png new file mode 100644 index 00000000..303737bf Binary files /dev/null and b/assets/images/png/smartwatch_services_image.png differ diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index fe4c04d8..bf1aedd0 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -301,7 +301,7 @@ "vitalSignsSubTitle": "التقارير", "myMedical": "نشط", "myMedicalSubtitle": "الأدوية", - "myDoctor": "طبيبي", + "myDoctor": "أطبائي", "myDoctorSubtitle": "القائمة", "eye": "العين", "eyeSubtitle": "القياس", @@ -1618,5 +1618,35 @@ "sortByLocation": "الترتيب حسب الموقع", "timeForFirstReminder": "وقت التذكير الأول", "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": "اتصل للحصول على المساعدة الفورية" + } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 9419b5a3..61245515 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -300,7 +300,7 @@ "vitalSignsSubTitle": "Reports", "myMedical": "Active", "myMedicalSubtitle": "Medications", - "myDoctor": "My Doctor", + "myDoctor": "My Doctors", "myDoctorSubtitle": "List", "eye": "Eye", "eyeSubtitle": "Measurement", @@ -1611,5 +1611,33 @@ "sortByLocation": "Sort by location", "sortByNearestLocation": "Sort by nearest to your location", "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" } diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 2de4ce5c..374ac9d1 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -200,7 +200,7 @@ class ApiClientImp implements ApiClient { } // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 1231755; + // body['PatientID'] = 1018977; // body['PatientTypeID'] = 1; // body['PatientOutSA'] = 0; // body['SessionID'] = "45786230487560q"; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 73cb287f..63d36252 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -4,7 +4,7 @@ import 'package:hmg_patient_app_new/core/enums.dart'; class ApiConsts { 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 diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index c4c3c366..81226f86 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -366,6 +366,10 @@ class AppAssets { static const String emergencyService = '$pngBasePath/emergency_services_image.png'; static const String cmcService = '$pngBasePath/cmc_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 femaleImg = '$pngBasePath/female_img.png'; diff --git a/lib/core/utils/loading_utils.dart b/lib/core/utils/loading_utils.dart index bd24d30c..48d4f2dd 100644 --- a/lib/core/utils/loading_utils.dart +++ b/lib/core/utils/loading_utils.dart @@ -15,37 +15,37 @@ class LoadingUtils { static bool get isLoading => _isLoadingVisible; - static showFullScreenLoader({bool barrierDismissible = true, isSuccessDialog = false, String loadingText = "Loading, Please wait..."}) { - if (!_isLoadingVisible) { - _isLoadingVisible = true; - final context = _navigationService.navigatorKey.currentContext; - log("got the context in showFullScreenLoading"); - if (context == null) return; - - showDialog( - barrierDismissible: barrierDismissible, - context: context, - barrierColor: AppColors.blackColor.withOpacity(0.5), - useRootNavigator: false, - useSafeArea: false, - builder: (BuildContext context) { - return Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: false, - ), - child: Material( - child: Center( - child: isSuccessDialog ? Utils.getSuccessWidget(loadingText: loadingText) : Utils.getLoadingWidget(loadingText: loadingText), - ).paddingSymmetrical(24.w, 0), - ), - ); - }).then((value) { - _isLoadingVisible = false; - }); - } - } + // static showFullScreenLoader({bool barrierDismissible = true, isSuccessDialog = false, String loadingText = "Loading, Please wait..."}) { + // if (!_isLoadingVisible) { + // _isLoadingVisible = true; + // final context = _navigationService.navigatorKey.currentContext; + // log("got the context in showFullScreenLoading"); + // if (context == null) return; + // + // showDialog( + // barrierDismissible: barrierDismissible, + // context: context, + // barrierColor: AppColors.blackColor.withOpacity(0.5), + // useRootNavigator: false, + // useSafeArea: false, + // builder: (BuildContext context) { + // return Container( + // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + // color: AppColors.whiteColor, + // borderRadius: 20.h, + // hasShadow: false, + // ), + // child: Material( + // child: Center( + // child: isSuccessDialog ? Utils.getSuccessWidget(loadingText: loadingText) : Utils.getLoadingWidget(loadingText: loadingText), + // ).paddingSymmetrical(24.w, 0), + // ), + // ); + // }).then((value) { + // _isLoadingVisible = false; + // }); + // } + // } static hideFullScreenLoader() { if (!_isLoadingVisible) return; diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index f63b160b..1f09cdfd 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -768,6 +768,7 @@ class Utils { return Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 5.w, children: [ Image.asset(AppAssets.mada, width: 35.h, height: 35.h), Image.asset( diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 3ad0ad92..407380c1 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -73,13 +73,14 @@ extension EmailValidator on String { int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow, - double letterSpacing = 0}) => + double letterSpacing = 0, bool isEnglishOnly = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, maxLines: maxlines, overflow: textOverflow, style: TextStyle( + fontFamily: isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins', fontSize: 9.f, fontStyle: fontStyle ?? FontStyle.normal, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), @@ -89,7 +90,7 @@ extension EmailValidator on String { 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, textAlign: isCenter ? TextAlign.center : null, maxLines: (maxLine > 0) ? maxLine : null, @@ -100,6 +101,7 @@ extension EmailValidator on String { color: color ?? AppColors.blackColor, letterSpacing: letterSpacing, decoration: isUnderLine ? TextDecoration.underline : null, + fontFamily: isEnglishOnly ? "Poppins" : getIt.get().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), ); - 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, textAlign: isCenter ? TextAlign.center : null, style: TextStyle( + fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().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)), ); diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index c0bc4dd1..6072e8b9 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -862,17 +862,17 @@ class AuthenticationViewModel extends ChangeNotifier { resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async { if (apiResponse.data is String) { //TODO: This Section Need to Be Testing. - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); _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. } else { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); 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. await clearDefaultInputValues(); // This will Clear All Default Values Of User. Future.delayed(Duration(seconds: 1), () { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); // _navigationService.pushAndReplace(AppRoutes.loginScreen); _navigationService.pushAndRemoveUntil(CustomPageRoute(page: LandingNavigation()), (r) => false); _navigationService.push(CustomPageRoute(page: LoginScreen())); diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index afc9b568..2d077b5f 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:easy_localization/easy_localization.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/dependencies.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/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.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'; @@ -139,6 +141,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { bool applyFilters = false; bool isWaitingAppointmentAvailable = false; + bool isPatientRescheduleAppointment = false; bool isWaitingAppointmentSelected = false; int waitingAppointmentProjectID = 0; @@ -157,7 +160,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { LaserCategoryType(11, 'retouch'), ]; List maleLaserCategory = [ - LaserCategoryType(1, 'body'), + LaserCategoryType(1, 'bodyString'), LaserCategoryType(2, 'face'), LaserCategoryType(11, 'retouch'), ]; @@ -289,6 +292,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { isContinueDentalPlan = false; isChiefComplaintsListLoading = true; isWaitingAppointmentSelected = false; + isPatientRescheduleAppointment = false; bodyTypes = [maleLaserCategory, femaleLaserCategory]; // getLocation(); notifyListeners(); @@ -427,6 +431,11 @@ class BookAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } + setIsPatientRescheduleAppointment(bool isPatientRescheduleAppointment) { + this.isPatientRescheduleAppointment = isPatientRescheduleAppointment; + notifyListeners(); + } + void onTabChanged(int index) { calculationID = null; isGetDocForHealthCal = false; @@ -652,12 +661,14 @@ class BookAppointmentsViewModel extends ChangeNotifier { notifyListeners(); // Check if doctor is favorite after getting profile - checkIsFavouriteDoctor( - patientID: _appState.getAuthenticatedUser()!.patientId!, - projectID: doctorsProfileResponseModel.projectID ?? 0, - clinicID: doctorsProfileResponseModel.clinicID ?? 0, - doctorID: doctorsProfileResponseModel.doctorID ?? 0, - ); + if(_appState.isAuthenticated) { + checkIsFavouriteDoctor( + patientID: _appState.getAuthenticatedUser()!.patientId!, + projectID: doctorsProfileResponseModel.projectID ?? 0, + clinicID: doctorsProfileResponseModel.clinicID ?? 0, + doctorID: doctorsProfileResponseModel.doctorID ?? 0, + ); + } if (onSuccess != null) { onSuccess(apiResponse); @@ -852,64 +863,114 @@ class BookAppointmentsViewModel extends ChangeNotifier { print(failure); onError!(failure.message); }, - (apiResponse) { + (apiResponse) async { if (apiResponse.messageStatus == 2) { // onError!(apiResponse); - LoadingUtils.hideFullScreenLoader(); - 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 { + if (isPatientRescheduleAppointment) { + 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'], + ); + + 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(); - Future.delayed(Duration(milliseconds: 50)).then((value) async {}); - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: LocaleKeys.bookingYourAppointment.tr()); - await insertSpecificAppointment( - onError: (err) {}, - onSuccess: (apiResp) async { - LoadingUtils.hideFullScreenLoader(); - await Future.delayed(Duration(milliseconds: 50)).then((value) async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); - await Future.delayed(Duration(milliseconds: 4000)).then((value) { - LoadingUtils.hideFullScreenLoader(); - Navigator.pushAndRemoveUntil( - navigationService.navigatorKey.currentContext!, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); + await Future.delayed(Duration(milliseconds: 50)).then((value) async { + showCommonBottomSheetWithoutHeight( + GetIt.instance().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 + ); + }); + }); + }); + } 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().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: () {}, - isFullScreen: false, - isCloseButtonVisible: true, - ); + }); + }), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } } else if (apiResponse.messageStatus == 1) { if (apiResponse.data == null || apiResponse.data!.isEmpty) { onError!("No free slots available".tr()); @@ -946,7 +1007,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { (apiResponse) { if (apiResponse.messageStatus == 2) { // onError!(apiResponse); - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), navigationService.navigatorKey.currentContext!, @@ -982,22 +1043,28 @@ class BookAppointmentsViewModel extends ChangeNotifier { await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { navigationService.pop(); 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( onError: (err) {}, onSuccess: (apiResp) async { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); await Future.delayed(Duration(milliseconds: 50)).then((value) async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); - await Future.delayed(Duration(milliseconds: 4000)).then((value) { - LoadingUtils.hideFullScreenLoader(); - Navigator.pushAndRemoveUntil( - navigationService.navigatorKey.currentContext!, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); - }); + showCommonBottomSheetWithoutHeight( + GetIt.instance().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 + ); }); }); }); @@ -1491,7 +1558,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { }, (apiResponse) { if (apiResponse.messageStatus == 2) { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), navigationService.navigatorKey.currentContext!, diff --git a/lib/features/book_appointments/models/resp_models/laser_body_parts.dart b/lib/features/book_appointments/models/resp_models/laser_body_parts.dart index d7d3fbd1..dcb8a70e 100644 --- a/lib/features/book_appointments/models/resp_models/laser_body_parts.dart +++ b/lib/features/book_appointments/models/resp_models/laser_body_parts.dart @@ -61,4 +61,14 @@ class LaserBodyPart { data['CategoryNameN'] = this.categoryNameN; 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; } diff --git a/lib/features/hmg_services/hmg_services_repo.dart b/lib/features/hmg_services/hmg_services_repo.dart index 89285339..b70e024c 100644 --- a/lib/features/hmg_services/hmg_services_repo.dart +++ b/lib/features/hmg_services/hmg_services_repo.dart @@ -919,8 +919,6 @@ class HmgServicesRepoImp implements HmgServicesRepo { @override Future>>> getPatientVitalSign() async { Map requestBody = { - - }; try { diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart index 0e7efdb3..de543970 100644 --- a/lib/features/lab/lab_view_model.dart +++ b/lib/features/lab/lab_view_model.dart @@ -4,6 +4,7 @@ import 'dart:core'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/common_models/data_points.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; @@ -71,6 +72,9 @@ class LabViewModel extends ChangeNotifier { List uniqueTestsList = []; List indexedCharacterForUniqueTest = []; + List filteredUniqueTestsList = []; + List filteredIndexedCharacterForUniqueTest = []; + double maxY = 0.0; double minY = double.infinity; double maxX = double.infinity; @@ -84,6 +88,9 @@ class LabViewModel extends ChangeNotifier { LabOrderResponseByAi? labOrderResponseByAi; LabOrdersResponseByAi? labOrdersResponseByAi; + bool isLabAIAnalysisNeedsToBeShown = true; + bool isLabResultsHistoryShowMore = false; + LabViewModel({required this.labRepo, required this.errorHandlerService, required this.navigationService}); initLabProvider() { @@ -93,6 +100,8 @@ class LabViewModel extends ChangeNotifier { labOrderTests.clear(); isLabOrdersLoading = true; isLabResultsLoading = true; + isLabAIAnalysisNeedsToBeShown = true; + isLabResultsHistoryShowMore = false; patientLabOrdersByClinic.clear(); patientLabOrdersByHospital.clear(); patientLabOrdersViewList.clear(); @@ -111,6 +120,16 @@ class LabViewModel extends ChangeNotifier { notifyListeners(); } + setIsLabAIAnalysisNeedsToBeShown(bool isLabAIAnalysisNeedsToBeShown) { + this.isLabAIAnalysisNeedsToBeShown = isLabAIAnalysisNeedsToBeShown; + notifyListeners(); + } + + setIsLabResultsHistoryShowMore() { + isLabResultsHistoryShowMore = !isLabResultsHistoryShowMore; + notifyListeners(); + } + void setIsSortByClinic(bool value) { isSortByClinic = value; patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital; @@ -218,7 +237,8 @@ class LabViewModel extends ChangeNotifier { final clinicMap = >{}; final hospitalMap = >{}; if (query.isEmpty) { - // filteredLabOrders = List.from(patientLabOrders); // reset + filteredLabOrders = List.from(patientLabOrders); // reset + filteredUniqueTestsList = List.from(uniqueTestsList); for (var order in patientLabOrders) { final clinicKey = (order.clinicDescription ?? 'Unknown').trim(); clinicMap.putIfAbsent(clinicKey, () => []).add(order); @@ -234,7 +254,10 @@ class LabViewModel extends ChangeNotifier { final descriptions = order.testDetails?.map((d) => d.description?.toLowerCase()).toList() ?? []; return descriptions.any((desc) => desc != null && desc.contains(query.toLowerCase())); }).toList(); - // patientLabOrders = filteredLabOrders; + filteredUniqueTestsList = uniqueTestsList.where((test) { + final desc = test.description?.toLowerCase() ?? ''; + return desc.contains(query.toLowerCase()); + }).toList(); for (var order in filteredLabOrders) { final clinicKey = (order.clinicDescription ?? 'Unknown').trim(); clinicMap.putIfAbsent(clinicKey, () => []).add(order); @@ -246,6 +269,14 @@ class LabViewModel extends ChangeNotifier { patientLabOrdersByHospital = hospitalMap.values.toList(); 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(); } @@ -280,6 +311,10 @@ class LabViewModel extends ChangeNotifier { for (var element in uniqueTests) { labOrderTests.add(element.description ?? ""); } + + // Initialize filtered lists with full data + filteredUniqueTestsList = List.from(uniqueTestsList); + filteredIndexedCharacterForUniqueTest = List.from(indexedCharacterForUniqueTest); } Future getLabResultsByAppointmentNo( @@ -396,7 +431,7 @@ class LabViewModel extends ChangeNotifier { double lowRefenceValue = double.infinity; String? flagForLowReferenceRange; - sortedResult.take(3).toList().reversed.forEach((element) { + sortedResult.toList().reversed.forEach((element) { try { var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!); var resultValue = double.parse(element.resultValue!); @@ -787,7 +822,13 @@ class LabViewModel extends ChangeNotifier { Future 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: loadingText); + // LoadingUtils.showFullScreenLoader(loadingText: loadingText); + LoaderBottomSheet.showLoader( + loadingText: loadingText, + showCloseButton: true, + onCloseTap: () { + setIsLabAIAnalysisNeedsToBeShown(false); + }); List> results = []; Map orderData = {"order_date": labOrder.orderDate ?? "", "clinic": labOrder.clinicDescription ?? "", "doctor": labOrder.doctorName ?? "", "results": []}; List> testResults = []; @@ -810,16 +851,20 @@ class LabViewModel extends ChangeNotifier { result.fold( (failure) async { LoadingUtils.hideFullScreenLoader(); - await errorHandlerService.handleError(failure: failure); + if (isLabAIAnalysisNeedsToBeShown) { + await errorHandlerService.handleError(failure: failure); + } }, (apiResponse) { LoadingUtils.hideFullScreenLoader(); if (apiResponse.messageStatus == 2) { } else if (apiResponse.messageStatus == 1) { - labOrdersResponseByAi = apiResponse.data; - navigationService.push( - MaterialPageRoute(builder: (_) => LabAiAnalysisDetailedPage()), - ); + if (isLabAIAnalysisNeedsToBeShown) { + labOrdersResponseByAi = apiResponse.data; + navigationService.push( + MaterialPageRoute(builder: (_) => LabAiAnalysisDetailedPage()), + ); + } } }, ); @@ -828,7 +873,12 @@ class LabViewModel extends ChangeNotifier { } Future 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> results = []; results.add({ "Description": recentLabResult.description ?? '', @@ -838,19 +888,24 @@ class LabViewModel extends ChangeNotifier { "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().getAuthenticatedUser()!.patientId, "language_id": langId, "lab_results": results}; final result = await labRepo.getPatientAiOverViewLabOrder(payload); result.fold( (failure) async { LoaderBottomSheet.hideLoader(); - await errorHandlerService.handleError(failure: failure); + if (isLabAIAnalysisNeedsToBeShown) { + await errorHandlerService.handleError(failure: failure); + } }, (apiResponse) { LoaderBottomSheet.hideLoader(); if (apiResponse.messageStatus == 2) { } else if (apiResponse.messageStatus == 1) { - labOrderResponseByAi = apiResponse.data; - notifyListeners(); + if (isLabAIAnalysisNeedsToBeShown) { + labOrderResponseByAi = apiResponse.data; + notifyListeners(); + } } }, ); diff --git a/lib/features/my_appointments/appointment_via_region_viewmodel.dart b/lib/features/my_appointments/appointment_via_region_viewmodel.dart index 10da4e3d..1144be82 100644 --- a/lib/features/my_appointments/appointment_via_region_viewmodel.dart +++ b/lib/features/my_appointments/appointment_via_region_viewmodel.dart @@ -87,6 +87,8 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier { hmcCount = registeredDoctorMap.hmcSize; hmgCount = registeredDoctorMap.hmgSize; + hospitalList!.sort((a, b) => num.parse(a.distanceInKMs!).compareTo(num.parse(b.distanceInKMs!))); + getDisplayList(); } diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index ccf76ce0..0f33138a 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -513,7 +513,10 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { try { final list = response['PatientDoctorAppointmentResultList']; - final appointmentsList = list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map)).toList().cast(); + List appointmentsList = + list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map)).toList().cast(); + + // appointmentsList.removeWhere((element) => element.isActiveDoctorProfile == false); apiResponse = GenericApiModel>( messageStatus: messageStatus, diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index aa56d440..a1c48867 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -94,6 +94,10 @@ class MyAppointmentsViewModel extends ChangeNotifier { selectedTabIndex = index; start = null; end = null; + // if (index == 0) { + // filteredAppointmentList.clear(); + // filteredAppointmentList.addAll(patientAppointmentsHistoryList); + // } notifyListeners(); } diff --git a/lib/features/notifications/notifications_repo.dart b/lib/features/notifications/notifications_repo.dart index 853a9792..a4588042 100644 --- a/lib/features/notifications/notifications_repo.dart +++ b/lib/features/notifications/notifications_repo.dart @@ -12,6 +12,10 @@ abstract class NotificationsRepo { required int pagingSize, required int currentPage, }); + + Future>>> markAsRead({ + required int notificationID, + }); } class NotificationsRepoImp implements NotificationsRepo { @@ -75,4 +79,38 @@ class NotificationsRepoImp implements NotificationsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> markAsRead({required int notificationID}) async { + Map mapDevice = {"NotificationPoolID": notificationID}; + + try { + GenericApiModel>? 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>( + 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())); + } + } } diff --git a/lib/features/notifications/notifications_view_model.dart b/lib/features/notifications/notifications_view_model.dart index e15e267c..ea552292 100644 --- a/lib/features/notifications/notifications_view_model.dart +++ b/lib/features/notifications/notifications_view_model.dart @@ -7,13 +7,15 @@ class NotificationsViewModel extends ChangeNotifier { bool isNotificationsLoading = false; bool hasMoreNotifications = true; + int unreadNotificationsCount = 0; + NotificationsRepo notificationsRepo; ErrorHandlerService errorHandlerService; List notificationsList = []; int currentPage = 0; - int pagingSize = 14; + int pagingSize = 15; int notificationStatusID = 2; // Default to status 2 (e.g., unread) NotificationsViewModel({ @@ -46,6 +48,7 @@ class NotificationsViewModel extends ChangeNotifier { Function(String)? onError, }) async { isNotificationsLoading = true; + unreadNotificationsCount = 0; notifyListeners(); final result = await notificationsRepo.getAllNotifications( @@ -78,6 +81,11 @@ class NotificationsViewModel extends ChangeNotifier { } notificationsList.addAll(newNotifications); + for (var notification in notificationsList) { + if (notification.isRead == false) { + unreadNotificationsCount++; + } + } currentPage++; notifyListeners(); @@ -89,6 +97,21 @@ class NotificationsViewModel extends ChangeNotifier { ); } + Future 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 refreshNotifications({ Function(dynamic)? onSuccess, Function(String)? onError, diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index d3fcd24a..285567e1 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1597,6 +1597,7 @@ abstract class LocaleKeys { static const timeFor = 'timeFor'; static const hmgPolicies = 'hmgPolicies'; static const darkMode = 'darkMode'; + static const featureComingSoonDescription = 'featureComingSoonDescription'; static const generateAiAnalysisResult = 'generateAiAnalysisResult'; static const ratings = 'ratings'; static const hmgPharmacyText = 'hmgPharmacyText'; @@ -1611,6 +1612,33 @@ abstract class LocaleKeys { static const timeForFirstReminder = 'timeForFirstReminder'; static const reminderRemovalNote = 'reminderRemovalNote'; 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'; } diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 176b3771..dde08085 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -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/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/home/navigation_screen.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/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; @@ -73,10 +75,10 @@ class _AppointmentDetailsPageState extends State { void initState() { scheduleMicrotask(() async { if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) { - CalenderUtilsNew calendarUtils = await CalenderUtilsNew.instance; - var doesExist = await calendarUtils.checkIfEventExist("${widget.patientAppointmentHistoryResponseModel.appointmentNo}"); - myAppointmentsViewModel.setAppointmentReminder(doesExist, widget.patientAppointmentHistoryResponseModel); - setState(() {}); + // CalenderUtilsNew calendarUtils = await CalenderUtilsNew.instance; + // var doesExist = await calendarUtils.checkIfEventExist("${widget.patientAppointmentHistoryResponseModel.appointmentNo}"); + // myAppointmentsViewModel.setAppointmentReminder(doesExist, widget.patientAppointmentHistoryResponseModel); + // setState(() {}); } }); @@ -148,41 +150,57 @@ class _AppointmentDetailsPageState extends State { }); }, onCancelTap: () async { - LoaderBottomSheet.showLoader(loadingText: LocaleKeys.cancellingAppointmentPleaseWait.tr(context: context)); - await myAppointmentsViewModel.cancelAppointment( - patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, - onSuccess: (apiResponse) { - LoaderBottomSheet.hideLoader(); - myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); - myAppointmentsViewModel.getPatientAppointments(true, false); - showCommonBottomSheetWithoutHeight( - context, - child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentCancelledSuccessfully.tr(context: context)), - callBackFunc: () { - Navigator.of(context).pop(); - }, - title: "", - isCloseButtonVisible: true, - isDismissible: false, - isFullScreen: false, - ); - }, - onError: (err) { - LoaderBottomSheet.hideLoader(); - showCommonBottomSheetWithoutHeight( - context, - child: Utils.getErrorWidget(loadingText: err.toString()), - callBackFunc: () {}, - title: "", - isCloseButtonVisible: true, - isDismissible: false, - isFullScreen: false, - ); - }); - var isEventAddedOrRemoved = await CalenderUtilsNew.instance.checkAndRemove( id:"${widget.patientAppointmentHistoryResponseModel.appointmentNo}", ); - setState(() { - myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel); - }); + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.notice.tr(context: context), + context, + child: Utils.getWarningWidget( + loadingText: LocaleKeys.cancelAppointmentConfirmMessage.tr(context: context), + isShowActionButtons: true, + onCancelTap: () { + Navigator.of(context).pop(); + }, + onConfirmTap: () async { + Navigator.of(context).pop(); + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.cancellingAppointmentPleaseWait.tr(context: context)); + myAppointmentsViewModel.onTabChange(0); + myAppointmentsViewModel.updateListWRTTab(0); + await myAppointmentsViewModel.cancelAppointment( + patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, + onSuccess: (apiResponse) async { + LoaderBottomSheet.hideLoader(); + myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); + myAppointmentsViewModel.getPatientAppointments(true, false); + showCommonBottomSheet( + context, + child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentCancelledSuccessfully.tr(context: context)), + callBackFunc: (str) { + Navigator.of(context).pop(); + }, + title: "", + isCloseButtonVisible: false, + isDismissible: false, + isFullScreen: false, + isAutoDismiss: true, + height: ResponsiveExtension.screenHeight * 0.3, + ); + }, + 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 { openDoctorScheduleCalendar(); @@ -229,16 +247,22 @@ class _AppointmentDetailsPageState extends State { LoaderBottomSheet.hideLoader(); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.initAppointmentsViewModel(); - myAppointmentsViewModel.getPatientAppointments(true, false); + // myAppointmentsViewModel.getPatientAppointments(true, false); showCommonBottomSheetWithoutHeight( title: "", context, child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), callBackFunc: () { - Navigator.of(context).pop(); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); }, isFullScreen: false, - isCloseButtonVisible: true, + isCloseButtonVisible: false, + isAutoDismiss: true ); }); }, @@ -987,12 +1011,13 @@ class _AppointmentDetailsPageState extends State { doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL, doctorTitle: widget.patientAppointmentHistoryResponseModel.doctorTitle, name: widget.patientAppointmentHistoryResponseModel.doctorNameObj, - nationalityFlagURL: "https://hmgwebservices.com/Images/flag/SYR.png", + nationalityFlagURL: "", speciality: [], clinicName: widget.patientAppointmentHistoryResponseModel.clinicName, projectName: widget.patientAppointmentHistoryResponseModel.projectName, ); bookAppointmentsViewModel.setSelectedDoctor(doctor); + bookAppointmentsViewModel.setIsPatientRescheduleAppointment(true); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingDoctorSchedulePleaseWait.tr(context: context)); await bookAppointmentsViewModel.getDoctorFreeSlots( isBookingForLiveCare: false, @@ -1036,17 +1061,19 @@ class _AppointmentDetailsPageState extends State { myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.getPatientAppointments(true, false); Navigator.of(context).pop(); - }, - title: "", - height: ResponsiveExtension.screenHeight * 0.3, - isCloseButtonVisible: true, - isDismissible: false, - isFullScreen: false, + }, title: "", height: ResponsiveExtension.screenHeight * 0.3, isAutoDismiss: true, isCloseButtonVisible: true, isDismissible: false, isFullScreen: false, isSuccessDialog: true); }); // LoaderBottomSheet.hideLoader(); case 15: - break; + getIt.get().showCommonBottomSheetWithoutH( + message: LocaleKeys.upcomingPaymentPending.tr(context: context), + label: LocaleKeys.notice.tr(), + onOkPressed: () {}, + okLabel: "confirm", + cancelLabel: LocaleKeys.acknowledged.tr(context: context), + isConfirmButton: true, + ); case 20: myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true); Navigator.of(context).push( diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart index c9e70487..71bf30bf 100644 --- a/lib/presentation/appointments/my_appointments_page.dart +++ b/lib/presentation/appointments/my_appointments_page.dart @@ -55,36 +55,40 @@ class _MyAppointmentsPageState extends State { bookAppointmentsViewModel = Provider.of(context, listen: false); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, - body: CollapsingListView( - title: LocaleKeys.appointmentsList.tr(context: context), - child: SingleChildScrollView( - child: Column( - children: [ - SizedBox(height: 16.h), - CustomTabBar( - activeTextColor: Color(0xffED1C2B), - activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), - tabs: [ - CustomTabBarModel(null, LocaleKeys.allAppt.tr(context: context)), - CustomTabBarModel(null, LocaleKeys.upcoming.tr(context: context)), - CustomTabBarModel(null, LocaleKeys.completed.tr(context: context)), - ], - onTabChange: (index) { - setState(() { - expandedIndex = null; - }); - myAppointmentsViewModel.onTabChange(index); - myAppointmentsViewModel.updateListWRTTab(index); - context.read().flush(); - }, - ).paddingSymmetrical(24.h, 0.h), - Consumer(builder: (context, myAppointmentsVM, child) { - return getSelectedTabData(myAppointmentsVM.selectedTabIndex, myAppointmentsVM); - }), - ], + body: Consumer(builder: (context, myAppointmentsVM, child) { + return CollapsingListView( + title: LocaleKeys.appointmentsList.tr(context: context), + child: SingleChildScrollView( + child: Column( + children: [ + SizedBox(height: 16.h), + CustomTabBar( + initialIndex: myAppointmentsVM.selectedTabIndex, + activeTextColor: Color(0xffED1C2B), + activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), + tabs: [ + CustomTabBarModel(null, LocaleKeys.allAppt.tr(context: context)), + CustomTabBarModel(null, LocaleKeys.upcoming.tr(context: context)), + CustomTabBarModel(null, LocaleKeys.completed.tr(context: context)), + ], + onTabChange: (index) { + setState(() { + expandedIndex = null; + }); + myAppointmentsViewModel.onTabChange(index); + myAppointmentsViewModel.updateListWRTTab(index); + context.read().flush(); + }, + ).paddingSymmetrical(24.h, 0.h), + // Consumer(builder: (context, myAppointmentsVM, child) { + // return + getSelectedTabData(myAppointmentsVM.selectedTabIndex, myAppointmentsVM), + // }), + ], ), - ), - ), + ), + ); + }), ); } @@ -144,7 +148,7 @@ class _MyAppointmentsPageState extends State { ? myAppointmentsVM.patientAppointmentsViewList.length : 1, itemBuilder: (context, index) { - final isExpanded = expandedIndex == index; + final isExpanded = myAppointmentsVM.selectedTabIndex == 1 ? true : expandedIndex == index; return myAppointmentsVM.isMyAppointmentsLoading ? Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), diff --git a/lib/presentation/appointments/my_doctors_page.dart b/lib/presentation/appointments/my_doctors_page.dart index 002f537c..26e5927b 100644 --- a/lib/presentation/appointments/my_doctors_page.dart +++ b/lib/presentation/appointments/my_doctors_page.dart @@ -218,8 +218,17 @@ class _MyDoctorsPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - AppCustomChipWidget(labelText: "${group.length} ${'doctors'}"), - Icon(isExpanded ? Icons.expand_less : Icons.expand_more), + AppCustomChipWidget( + 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), @@ -289,10 +298,10 @@ class _MyDoctorsPageState extends State { flex: 2, child: CustomButton( icon: AppAssets.view_report_icon, - iconColor: AppColors.primaryRedColor, + iconColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 1.0) : AppColors.primaryRedColor, iconSize: 16.h, text: LocaleKeys.viewProfile.tr(context: context), - onPressed: () async { + onPressed: doctor?.isActiveDoctorProfile == false ? () {} : () async { bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel( clinicID: doctor?.clinicID ?? 0, projectID: doctor?.projectID ?? 0, @@ -323,9 +332,9 @@ class _MyDoctorsPageState extends State { ); }); }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, + backgroundColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 0.3) : AppColors.secondaryLightRedColor, + borderColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 0.01) : AppColors.secondaryLightRedColor, + textColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 1.0) : AppColors.primaryRedColor, fontSize: 14, fontWeight: FontWeight.w500, borderRadius: 12, diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 52a1c219..c120a323 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -148,8 +148,8 @@ class AppointmentCard extends StatelessWidget { Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow), SizedBox(height: 2.h), (isFoldable || isTablet) - ? "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor) - : "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(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, isEnglishOnly: true), ], ), ).circle(100).toShimmer2(isShow: isLoading), @@ -456,7 +456,7 @@ class AppointmentCard extends StatelessWidget { doctorImageURL: patientAppointmentHistoryResponseModel.doctorImageURL, doctorTitle: patientAppointmentHistoryResponseModel.doctorTitle, name: patientAppointmentHistoryResponseModel.doctorNameObj, - nationalityFlagURL: 'https://hmgwebservices.com/Images/flag/SYR.png', + nationalityFlagURL: '', speciality: [], clinicName: patientAppointmentHistoryResponseModel.clinicName, projectName: patientAppointmentHistoryResponseModel.projectName, diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index 5a47708e..11619b5f 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -1,19 +1,27 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/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/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/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import '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 { const AppointmentDoctorCard( {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: [ Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow), 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), @@ -75,6 +83,7 @@ class AppointmentDoctorCard extends StatelessWidget { ), SizedBox(width: 16.w), Expanded( + flex: 9, child: Column( crossAxisAlignment: CrossAxisAlignment.start, 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().setSelectedDoctor(selectedDoctor); + LoaderBottomSheet.showLoader(); + await getIt.get().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), diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart index 96db5187..dcb2c765 100644 --- a/lib/presentation/authentication/saved_login_screen.dart +++ b/lib/presentation/authentication/saved_login_screen.dart @@ -84,7 +84,7 @@ class _SavedLogin extends State { SizedBox(height: 16.h), appState.getSelectDeviceByImeiRespModelElement != null ? 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(height: 24.h), Container( @@ -206,7 +206,7 @@ class _SavedLogin extends State { }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor, - textColor: AppColors.textColor, + textColor: Colors.white, icon: AppAssets.sms), ), Row( diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index cfdd9d6a..1ac49f85 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -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/my_appointments_view_model.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/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody; import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart'; @@ -119,18 +120,30 @@ class _BookAppointmentPageState extends State { if (appState.isAuthenticated) ...[ Consumer(builder: (context, myAppointmentsVM, child) { return myAppointmentsVM.isPatientMyDoctorsLoading - ? Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Image.network( + ? SizedBox( + height: 110.h, + child: ListView.separated( + 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", width: 64.w, height: 64.h, fit: BoxFit.cover, ).circle(100).toShimmer2(isShow: true, radius: 50.r), 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 ? SizedBox() @@ -138,7 +151,25 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ 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: 110.h, @@ -264,7 +295,46 @@ class _BookAppointmentPageState extends State { // Show empty state or actual list 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( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -659,7 +729,7 @@ class _BookAppointmentPageState extends State { return SizedBox.shrink(); } - void _handleSortByLocationToggle(bool value, AppointmentViaRegionViewmodel regionVM) { + void _handleSortByLocationToggle(bool value, AppointmentViaRegionViewmodel regionVM) { if (value) { final locationUtils = getIt.get(); locationUtils.getLocation( diff --git a/lib/presentation/book_appointment/doctor_profile_page.dart b/lib/presentation/book_appointment/doctor_profile_page.dart index 4c824a05..f9761c3a 100644 --- a/lib/presentation/book_appointment/doctor_profile_page.dart +++ b/lib/presentation/book_appointment/doctor_profile_page.dart @@ -33,17 +33,16 @@ class DoctorProfilePage extends StatelessWidget { children: [ Expanded( child: CollapsingListView( - title: LocaleKeys.doctorProfile.tr(), - trailing: Consumer( + title: LocaleKeys.profileOnlyText.tr(), + trailing: appState.isAuthenticated ? Consumer( builder: (context, viewModel, child) { return SizedBox( width: 24.h, height: 24.h, - child: Utils.buildSvgWithAssets( - icon: viewModel.isFavouriteDoctor ? AppAssets.bookmark_filled_icon : AppAssets.bookmark_icon, - width: 24.h, - height: 24.h, - iconColor: viewModel.isFavouriteDoctor ? AppColors.primaryRedColor : AppColors.textColor, + child: Icon( + viewModel.isFavouriteDoctor ? Icons.bookmark : Icons.bookmark_border_outlined, + size: 36.h, + color: viewModel.isFavouriteDoctor ? AppColors.primaryRedColor : AppColors.textColor, ).onPress(() async { viewModel.toggleFavouriteDoctor(); await viewModel.insertFavouriteDoctor( @@ -68,7 +67,7 @@ class DoctorProfilePage extends StatelessWidget { }), ); }, - ), + ) : SizedBox.shrink(), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -179,9 +178,10 @@ class DoctorProfilePage extends StatelessWidget { SizedBox(height: 16.h), Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.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), - 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), ), @@ -199,7 +199,12 @@ class DoctorProfilePage extends StatelessWidget { bookAppointmentsViewModel.selectedDoctor.speciality = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty; bookAppointmentsViewModel.selectedDoctor.specialityN = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty; 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(); bookAppointmentsViewModel.isLiveCareSchedule ? await bookAppointmentsViewModel.getLiveCareDoctorFreeSlots( diff --git a/lib/presentation/book_appointment/laser/laser_appointment.dart b/lib/presentation/book_appointment/laser/laser_appointment.dart index 3b94b0ec..c8773933 100644 --- a/lib/presentation/book_appointment/laser/laser_appointment.dart +++ b/lib/presentation/book_appointment/laser/laser_appointment.dart @@ -84,7 +84,7 @@ class LaserAppointment extends StatelessWidget { activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, LocaleKeys.malE.tr()), - CustomTabBarModel(null, "Female"), + CustomTabBarModel(null, LocaleKeys.femaleGender.tr()), ], onTabChange: (index) { var viewmodel = context.read(); @@ -93,6 +93,7 @@ class LaserAppointment extends StatelessWidget { viewmodel.getLaserClinic(); }, ), + SizedBox(height: 8.h), Selector( selector: (_, vm) => vm.selectedBodyTypeIndex, builder: (_, bodyType, __) { @@ -110,6 +111,7 @@ class LaserAppointment extends StatelessWidget { }); }, ), + SizedBox(height: 8.h), Selector>(selector: (_, vm) => vm.laserBodyPartsList, builder:(_, parts,__){ return BodyPartsListing( parts: parts, diff --git a/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart b/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart index c9578682..fb641327 100644 --- a/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart +++ b/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart @@ -1,9 +1,12 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.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/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/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:provider/provider.dart'; @@ -70,7 +73,7 @@ class BodyPartsListing extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ AspectRatio( - aspectRatio: 97 / 97, + aspectRatio: 130 / 120, child: FittedBox( fit: BoxFit.fitWidth, child: Stack( @@ -93,12 +96,12 @@ class BodyPartsListing extends StatelessWidget { Container( width: 18.h, height: 18.h, - child: Icon(Icons.done, - color: Colors.white, size: 12.h), decoration: BoxDecoration( color: AppColors.primaryRedColor, 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), Expanded( - child: Text( - context - .read() - .getLaserProcedureNameWRTLanguage(parts[index]), - style: TextStyle( - fontSize: 12.f, - fontWeight: FontWeight.w600, - color: Color(0xff2B353E), - letterSpacing: -0.48, + child: Center( + child: Column( + children: [ + context.read().getLaserProcedureNameWRTLanguage(parts[index]).toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + "${LocaleKeys.mins.tr()}: ".toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2), + parts[index].timeDuration!.toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2), + ], + ), + ], ), - maxLines: 1, ), ), ], diff --git a/lib/presentation/book_appointment/review_appointment_page.dart b/lib/presentation/book_appointment/review_appointment_page.dart index 9069138f..a9f07b05 100644 --- a/lib/presentation/book_appointment/review_appointment_page.dart +++ b/lib/presentation/book_appointment/review_appointment_page.dart @@ -90,12 +90,12 @@ class _ReviewAppointmentPageState extends State { .toString() .toText16(isBold: true, maxlines: 1), 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", width: 20.h, height: 15.h, fit: BoxFit.cover, - ), + ) : SizedBox.shrink(), ], ), SizedBox(height: 2.h), @@ -316,34 +316,42 @@ class _ReviewAppointmentPageState extends State { } 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); if (bookAppointmentsViewModel.isLiveCareSchedule) { await bookAppointmentsViewModel.insertSpecificAppointmentForLiveCare(onError: (err) { print(err.data["ErrorEndUserMessage"]); - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); }, onSuccess: (apiResp) async { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); await Future.delayed(Duration(milliseconds: 50)).then((value) async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); - await Future.delayed(Duration(milliseconds: 4000)).then((value) { - LoadingUtils.hideFullScreenLoader(); - bookAppointmentsViewModel.setIsLiveCareSchedule(false); - Navigator.pushAndRemoveUntil( - context, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); - }); + // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.appointmentSuccess.tr()); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()), + callBackFunc: () { + bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false); + bookAppointmentsViewModel.setIsLiveCareSchedule(false); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }, + isFullScreen: false, + isCloseButtonVisible: false, + isAutoDismiss: true + ); }); }); } else { //TODO: Add patient Derma package check API Here await bookAppointmentsViewModel.insertSpecificAppointment(onError: (err) { // print(err.data["ErrorEndUserMessage"]); - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( title: LocaleKeys.notice.tr(context: context), context, @@ -357,19 +365,25 @@ class _ReviewAppointmentPageState extends State { isCloseButtonVisible: true, ); }, onSuccess: (apiResp) async { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); await Future.delayed(Duration(milliseconds: 50)).then((value) async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); - await Future.delayed(Duration(milliseconds: 4000)).then((value) { - LoadingUtils.hideFullScreenLoader(); - bookAppointmentsViewModel.setIsLiveCareSchedule(false); - Navigator.pushAndRemoveUntil( - context, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); - }); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h), + callBackFunc: () { + bookAppointmentsViewModel.setIsLiveCareSchedule(false); + bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }, + isFullScreen: false, + isCloseButtonVisible: false, + isAutoDismiss: true + ); }); }); } diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index aaa52217..86d57ad2 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -1132,6 +1132,7 @@ class _SelectClinicPageState extends State { id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID?.toString() ?? ""; } if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) { + bookAppointmentsViewModel.setProjectID(id); if (appState.isAuthenticated) { initDentalAppointment(); return SizedBox.shrink(); diff --git a/lib/presentation/book_appointment/widgets/doctor_card.dart b/lib/presentation/book_appointment/widgets/doctor_card.dart index 25856829..4cf883e1 100644 --- a/lib/presentation/book_appointment/widgets/doctor_card.dart +++ b/lib/presentation/book_appointment/widgets/doctor_card.dart @@ -76,7 +76,7 @@ class DoctorCard extends StatelessWidget { children: [ Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, applyThemeColor: false), 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), @@ -134,6 +134,7 @@ class DoctorCard extends StatelessWidget { // richText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)) // .toText10(isEnglishOnly: true, color: AppColors.textColor), // backgroundColor: AppColors.successColor, + isEnglishOnly: true, textColor: AppColors.textColor, ).toShimmer2(isShow: isLoading) : SizedBox.shrink() diff --git a/lib/presentation/book_appointment/widgets/doctor_rating_details.dart b/lib/presentation/book_appointment/widgets/doctor_rating_details.dart index 1a5eedfe..c866b8a8 100644 --- a/lib/presentation/book_appointment/widgets/doctor_rating_details.dart +++ b/lib/presentation/book_appointment/widgets/doctor_rating_details.dart @@ -20,7 +20,7 @@ class DoctorRatingDetails extends StatelessWidget { : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - bookAppointmentsVM.doctorsProfileResponseModel.actualDoctorRate!.ceilToDouble().toString().toText44(isBold: true, isEnglishOnly: true), + bookAppointmentsVM.doctorsProfileResponseModel.decimalDoctorRate!.toString().toText44(isBold: true, isEnglishOnly: true), SizedBox(height: 4.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart b/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart index 840d38cf..8dcf6183 100644 --- a/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart +++ b/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart @@ -385,7 +385,7 @@ class _CmcSelectionReviewPageState extends State { isCloseButtonVisible: false, isDismissible: false, isFullScreen: false, - ); + isAutoDismiss: true); } void _handleConfirm() { diff --git a/lib/presentation/contact_us/contact_us.dart b/lib/presentation/contact_us/contact_us.dart index 602fc3b2..fbbd11bc 100644 --- a/lib/presentation/contact_us/contact_us.dart +++ b/lib/presentation/contact_us/contact_us.dart @@ -39,8 +39,8 @@ class ContactUs extends StatelessWidget { checkInOptionCard( AppAssets.call_fill, LocaleKeys.callNow.tr(), - // LocaleKeys.viewNearestHMGLocationsviewNearestHMGLocations.tr(), - "Call for immediate assistance", + LocaleKeys.callForAssistance.tr(), + // "Call for immediate assistance", ).onPress(() { launchUrl(Uri.parse("tel://" + "+966 92 006 6666")); }), @@ -83,6 +83,8 @@ class ContactUs extends StatelessWidget { LocaleKeys.liveChatWithHMG.tr(), ).onPress(() { if (getIt.get().isAuthenticated) { + Navigator.of(context).pop(); + contactUsViewModel.getLiveChatProjectsList(); Navigator.of(context).push( CustomPageRoute( page: LiveChatPage(), @@ -93,9 +95,9 @@ class ContactUs extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.liveChat.tr(), - serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, - serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, - serviceImage: AppAssets.eReferralService), + serviceHeader: LocaleKeys.liveChatServiceHeader.tr(), + serviceDescription: LocaleKeys.liveChatServiceDescription.tr(), + serviceImage: AppAssets.livechatService), ), ); } diff --git a/lib/presentation/contact_us/live_chat_page.dart b/lib/presentation/contact_us/live_chat_page.dart index 0350c316..98fc9954 100644 --- a/lib/presentation/contact_us/live_chat_page.dart +++ b/lib/presentation/contact_us/live_chat_page.dart @@ -121,7 +121,9 @@ class LiveChatPage extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, 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), Transform.flip( flipX: getIt.get().isArabic(), diff --git a/lib/presentation/emergency_services/RRT/rrt_request_type_select.dart b/lib/presentation/emergency_services/RRT/rrt_request_type_select.dart index c17f3560..12996bd6 100644 --- a/lib/presentation/emergency_services/RRT/rrt_request_type_select.dart +++ b/lib/presentation/emergency_services/RRT/rrt_request_type_select.dart @@ -21,6 +21,7 @@ class RrtRequestTypeSelect extends StatelessWidget { return Column( children: [ Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.rapidResponseTeam.tr(context: context).toText20(color: AppColors.textColor, isBold: true), SizedBox( @@ -32,10 +33,9 @@ class RrtRequestTypeSelect extends StatelessWidget { borderRadius: 20.r, ), child: OptionSelection(context)), - SizedBox( - height: 24.h, - ), + SizedBox(height: 16.h), termsAndCondition(context, emergencyServicesVM), + SizedBox(height: 16.h), ], ).paddingSymmetrical(24.w, 0.h), bottomPriceContent(context, emergencyServicesVM) @@ -122,15 +122,7 @@ class RrtRequestTypeSelect extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - 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), - ], - ), + Utils.getPaymentMethods(), Column( children: [ Divider( @@ -149,7 +141,9 @@ class RrtRequestTypeSelect extends StatelessWidget { CustomButton(text: LocaleKeys.next.tr(), onPressed: () { Navigator.pop(context); emergencyServicesVM.openRRT(); - }) + }, + isDisabled: !emergencyServicesVM.agreedToTermsAndCondition, + ) ], ).paddingAll(24.h), ), @@ -236,7 +230,6 @@ class RrtRequestTypeSelect extends StatelessWidget { value: emergencyServicesVM.agreedToTermsAndCondition, checkColor: AppColors.whiteColor, fillColor: MaterialStateProperty.resolveWith((Set states) { - print("the state is ${states}"); if (states.contains(WidgetState.selected)) { return AppColors.errorColor; } diff --git a/lib/presentation/emergency_services/emergency_services_page.dart b/lib/presentation/emergency_services/emergency_services_page.dart index 801f6ca2..c69f7eed 100644 --- a/lib/presentation/emergency_services/emergency_services_page.dart +++ b/lib/presentation/emergency_services/emergency_services_page.dart @@ -92,7 +92,10 @@ class _EmergencyServicesPageState extends State { ), ), 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(() { if(appState.isAuthenticated) { @@ -158,8 +161,8 @@ class _EmergencyServicesPageState extends State { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.emergencyServices.tr(), - serviceHeader: "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly".needTranslation, - 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, + serviceHeader: LocaleKeys.emergencyServiceHeader.tr(), + serviceDescription: LocaleKeys.emergencyServiceDescription.tr(), serviceImage: AppAssets.emergencyService), ), ); @@ -188,7 +191,10 @@ class _EmergencyServicesPageState extends State { ), ), 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(() { context.read().navigateToNearestERPage(); @@ -216,7 +222,7 @@ class _EmergencyServicesPageState extends State { ), ), 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(() { if(appState.isAuthenticated) { @@ -308,8 +314,8 @@ class _EmergencyServicesPageState extends State { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.emergencyServices.tr(), - serviceHeader: "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly".needTranslation, - 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, + serviceHeader: LocaleKeys.emergencyServiceHeader.tr(), + serviceDescription: LocaleKeys.emergencyServiceDescription.tr(), serviceImage: AppAssets.emergencyService), ), ); @@ -338,7 +344,7 @@ class _EmergencyServicesPageState extends State { ), ), 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(() { if(appState.isAuthenticated) { @@ -408,8 +414,8 @@ class _EmergencyServicesPageState extends State { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.emergencyServices.tr(), - serviceHeader: "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly".needTranslation, - 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, + serviceHeader: LocaleKeys.emergencyServiceHeader.tr(), + serviceDescription: LocaleKeys.emergencyServiceDescription.tr(), serviceImage: AppAssets.emergencyService), ), ); diff --git a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart index 9efcb01b..066e90cf 100644 --- a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart +++ b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart @@ -320,6 +320,7 @@ class _WalletPaymentConfirmPageState extends State { }, isFullScreen: false, isCloseButtonVisible: true, + isAutoDismiss: true ); }, onError: (err) { diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart index 8310e6db..22ea1121 100644 --- a/lib/presentation/hmg_services/services_page.dart +++ b/lib/presentation/hmg_services/services_page.dart @@ -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/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/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/habib_wallet/habib_wallet_view_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/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/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/emergency_services/emergency_services_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, bgColor: AppColors.bookAppointment, true, - route: AppRoutes.bookAppointmentPage, + route: null, + onTap: () { + getIt.get().onTabChanged(0); + Navigator.of(getIt().navigatorKey.currentContext!).push(CustomPageRoute(page: BookAppointmentPage())); + } ), HmgServicesComponentModel(5, LocaleKeys.completeCheckup.tr(), "", AppAssets.comprehensiveCheckup, bgColor: AppColors.bgGreenColor, true, route: null, onTap: () async { if (getIt.get().isAuthenticated) { @@ -90,8 +96,8 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.comprehensiveMedicalCheckup.tr(), - serviceHeader: "Complete Health Checkup: Advanced diagnostics, Detailed Health insights".needTranslation, - serviceDescription: "Get detailed insights into your health with our advanced diagnostics. Understand your body better for a healthier future.".needTranslation, + serviceHeader: LocaleKeys.cmcServiceHeader.tr(), + serviceDescription: LocaleKeys.cmcServiceDescription.tr(), serviceImage: AppAssets.cmcService), ), ); @@ -150,8 +156,8 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.eReferralServices.tr(), - serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, - serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, + serviceHeader: LocaleKeys.eReferralServiceHeader.tr(), + serviceDescription: LocaleKeys.eReferralServiceDescription.tr(), serviceImage: AppAssets.eReferralService), ), ); @@ -177,8 +183,8 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.bloodDonation.tr(), - serviceHeader: "Give Blood, Save Lives. Your donation makes a difference.".needTranslation, - 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, + serviceHeader: LocaleKeys.bloodDonationServiceHeader.tr(), + serviceDescription: LocaleKeys.bloodDonationServiceDescription.tr(), serviceImage: AppAssets.bloodDonationService), ), ); @@ -270,9 +276,9 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.healthTrackers.tr(), - serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, - serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, - serviceImage: AppAssets.eReferralService), + serviceHeader: LocaleKeys.healthTrackersServiceHeader.tr(), + serviceDescription: LocaleKeys.healthTrackersServiceDescription.tr(), + serviceImage: AppAssets.healthTrackersService), ), ); } @@ -311,8 +317,8 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.waterConsumption.tr(), - serviceHeader: "Stay Hydrated, Stay Healthy. Track your daily water intake with ease.".needTranslation, - 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, + serviceHeader: LocaleKeys.waterConsumptionServiceHeader.tr(), + serviceDescription: LocaleKeys.waterConsumptionServiceDescription.tr(), serviceImage: AppAssets.waterConsumptionService), ), ); @@ -353,9 +359,9 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.smartWatches.tr(), - serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, - serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, - serviceImage: AppAssets.eReferralService), + serviceHeader: LocaleKeys.smartWatchServiceHeader.tr(), + serviceDescription: LocaleKeys.smartWatchServiceDescription.tr(), + serviceImage: AppAssets.smartWatchService), ), ); } diff --git a/lib/presentation/home/data/landing_page_data.dart b/lib/presentation/home/data/landing_page_data.dart index 4606c800..33b12855 100644 --- a/lib/presentation/home/data/landing_page_data.dart +++ b/lib/presentation/home/data/landing_page_data.dart @@ -1,6 +1,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/home/data/service_card_data.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -123,8 +125,8 @@ class LandingPageData { ServiceCardData( serviceName: "my_doctors", icon: AppAssets.my_doctors_icon, - title: "My", - subtitle: "Doctors", + title: getIt.get().isArabic() ? LocaleKeys.myDoctor : "My", + subtitle: getIt.get().isArabic() ? "" : "Doctors", backgroundColor: AppColors.whiteColor, iconColor: AppColors.blackColor, textColor: AppColors.blackColor, diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 3109443f..f4cc5ac8 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -133,22 +133,22 @@ class _LandingPageState extends State { // Commented as per new requirement to remove rating popup from the app - // if(!appState.isRatedVisible) { - // appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) { - // if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) { - // appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!, - // onSuccess: ((response) { - // appointmentRatingViewModel.setClinicOrDoctor(false); - // appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation); - // appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation); - // openLastRating(); - // appState.setRatedVisible(true); - // }), - // ); - // } - // }, - // ); - // } + if(!appState.isRatedVisible) { + appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) { + if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) { + appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!, + onSuccess: ((response) { + appointmentRatingViewModel.setClinicOrDoctor(false); + appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr(context: context)); + appointmentRatingViewModel.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context)); + openLastRating(); + appState.setRatedVisible(true); + }), + ); + } + }, + ); + } } }); super.initState(); @@ -173,7 +173,7 @@ class _LandingPageState extends State { children: [ SingleChildScrollView( 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) : kToolbarHeight + 0.h, bottom: 24), @@ -365,6 +365,8 @@ class _LandingPageState extends State { ), ], ).paddingSymmetrical(24.h, 0.h).onPress(() { + myAppointmentsViewModel.onTabChange(0); + myAppointmentsViewModel.updateListWRTTab(0); Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage())); }), Consumer3( @@ -727,7 +729,7 @@ class _LandingPageState extends State { ], ), ), - (!insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown) + (appState.isAuthenticated && !insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown) ? Container( height: MediaQuery.paddingOf(context).top + 50.h, decoration: ShapeDecoration( diff --git a/lib/presentation/home/service_info_page.dart b/lib/presentation/home/service_info_page.dart index 5d07593d..bf21577a 100644 --- a/lib/presentation/home/service_info_page.dart +++ b/lib/presentation/home/service_info_page.dart @@ -63,6 +63,7 @@ class ServiceInfoPage extends StatelessWidget { child: Image.asset( serviceImage, fit: BoxFit.fitHeight, + height: 480.h, width: 520.w, ), ), diff --git a/lib/presentation/home/widgets/large_service_card.dart b/lib/presentation/home/widgets/large_service_card.dart index f0145afa..5428e21d 100644 --- a/lib/presentation/home/widgets/large_service_card.dart +++ b/lib/presentation/home/widgets/large_service_card.dart @@ -148,8 +148,8 @@ class LargeServiceCard extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.homeHealthCare.tr(), - serviceHeader: "Your Health, Elevated. Premium care, delivered to your doorstep.", - serviceDescription: "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.", + serviceHeader: LocaleKeys.homeHealthCareServiceHeader.tr(), + serviceDescription: LocaleKeys.homeHealthCareServiceDescription.tr(), serviceImage: AppAssets.homeHealthCareService), ), ); @@ -295,8 +295,8 @@ class FadedLargeServiceCard extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.homeHealthCare.tr(), - serviceHeader: "Your Health, Elevated. Premium care, delivered to your doorstep.".needTranslation, - serviceDescription: "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.".needTranslation, + serviceHeader: LocaleKeys.homeHealthCareServiceHeader.tr(), + serviceDescription: LocaleKeys.homeHealthCareServiceDescription.tr(), serviceImage: AppAssets.homeHealthCareService), ), ); diff --git a/lib/presentation/home/widgets/welcome_widget.dart b/lib/presentation/home/widgets/welcome_widget.dart index beff2ae4..b05aeb99 100644 --- a/lib/presentation/home/widgets/welcome_widget.dart +++ b/lib/presentation/home/widgets/welcome_widget.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; @@ -42,7 +43,11 @@ class WelcomeWidget extends StatelessWidget { children: [ Icon(Icons.menu, color: AppColors.textColor).onPress(() { getIt.get().getProfileSettings(); - Navigator.of(context).push(springPageRoute(ProfileSettings())); + Navigator.of(context).push( + CustomPageRoute( + page: ProfileSettings(), + ), + ); }), Image.asset(imageUrl, width: 40, height: 40), Column( @@ -58,7 +63,17 @@ class WelcomeWidget extends StatelessWidget { children: [ 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), - Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, height: 22.h, width: 22.w) + Transform.flip( + flipX: getIt.get().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) ], ), ], diff --git a/lib/presentation/insurance/insurance_home_page.dart b/lib/presentation/insurance/insurance_home_page.dart index 35e3acb8..1ac176e1 100644 --- a/lib/presentation/insurance/insurance_home_page.dart +++ b/lib/presentation/insurance/insurance_home_page.dart @@ -74,6 +74,7 @@ class _InsuranceHomePageState extends State { return Column( children: [ PatientInsuranceCard( + isShowButtons: index == 0, insuranceCardDetailsModel: insuranceVM.patientInsuranceList[index], isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo))), SizedBox( diff --git a/lib/presentation/insurance/widgets/insurance_update_details_card.dart b/lib/presentation/insurance/widgets/insurance_update_details_card.dart index 770f4da2..e7cfe059 100644 --- a/lib/presentation/insurance/widgets/insurance_update_details_card.dart +++ b/lib/presentation/insurance/widgets/insurance_update_details_card.dart @@ -130,6 +130,7 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget { }, isFullScreen: false, isCloseButtonVisible: false, + isAutoDismiss: true ); // Future.delayed(Duration(milliseconds: 2000)).then((value) async { // Navigator.pop(context); diff --git a/lib/presentation/insurance/widgets/patient_insurance_card.dart b/lib/presentation/insurance/widgets/patient_insurance_card.dart index ab70eb6f..d0f1b4db 100644 --- a/lib/presentation/insurance/widgets/patient_insurance_card.dart +++ b/lib/presentation/insurance/widgets/patient_insurance_card.dart @@ -20,10 +20,11 @@ import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; 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; bool isInsuranceExpired = false; + bool isShowButtons = true; late InsuranceViewModel insuranceViewModel; late AppState appState; @@ -60,7 +61,7 @@ class PatientInsuranceCard extends StatelessWidget { ), ], ), - AppCustomChipWidget( + isShowButtons ? AppCustomChipWidget( icon: isCurrentPatientInsuranceExpired(insuranceCardDetailsModel.cardValidTo!) ? AppAssets.cancel_circle_icon : insuranceViewModel.isInsuranceActive @@ -120,7 +121,7 @@ class PatientInsuranceCard extends StatelessWidget { ? AppColors.successColor.withOpacity(0.1) : AppColors.warningColorYellow.withOpacity(0.1), labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: insuranceViewModel.isInsuranceActive ? 6.w : 0.w), - ).toShimmer2(isShow: insuranceViewModel.isInsuranceLoading), + ).toShimmer2(isShow: insuranceViewModel.isInsuranceLoading) : SizedBox.shrink(), // AppCustomChipWidget( // icon: isInsuranceExpired ? AppAssets.cancel_circle_icon : AppAssets.insurance_active_icon, // labelText: isInsuranceExpired ? LocaleKeys.insuranceExpired.tr(context: context) : LocaleKeys.insuranceActive.tr(context: context), @@ -168,7 +169,7 @@ class PatientInsuranceCard extends StatelessWidget { ), SizedBox(height: 10.h), isInsuranceExpired - ? CustomButton( + ? isShowButtons ? CustomButton( icon: AppAssets.update_insurance_card_icon, iconColor: AppColors.warningColorYellow, iconSize: 15.h, @@ -194,7 +195,7 @@ class PatientInsuranceCard extends StatelessWidget { borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, - ) + ) : SizedBox.shrink() : Container(), ], ), diff --git a/lib/presentation/lab/alphabeticScroll.dart b/lib/presentation/lab/alphabeticScroll.dart index 95d9da45..933f399c 100644 --- a/lib/presentation/lab/alphabeticScroll.dart +++ b/lib/presentation/lab/alphabeticScroll.dart @@ -45,41 +45,55 @@ class _AlphabetScrollPageState extends State { void initState() { super.initState(); scheduleMicrotask((){ - for(var char in widget.alpahbetsAvailable){ - data[char] = widget.details.where((element)=>element.description?.toLowerCase().startsWith(char.toLowerCase()) == true).toList(); - - } - setState((){}); - + _rebuildData(); }); - 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) - final firstVisible = positions - .where((p) => p.itemTrailingEdge > 0) // visible - .reduce((min, p) => - p.itemLeadingEdge < min.itemLeadingEdge ? p : min); + if (positions.isEmpty) return; - if(_activeIndex == firstVisible.index) return ; - setState(() { - _activeIndex = firstVisible.index; - }); + // Get FIRST visible item (top-most) + final firstVisible = positions + .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 void dispose() { - itemPositionsListener.itemPositions.removeListener((){ - - }); + itemPositionsListener.itemPositions.removeListener(_onPositionChanged); super.dispose(); - } void _scrollToLetter(String letter) async { @@ -145,7 +159,6 @@ class _AlphabetScrollPageState extends State { crossAxisAlignment: CrossAxisAlignment.center, children:List.generate(widget.alpahbetsAvailable.length, (i) { final isActive = (i == _activeIndex); - return GestureDetector( onTap: () { setState(() => _activeIndex = i); @@ -161,7 +174,7 @@ class _AlphabetScrollPageState extends State { child: Opacity( opacity: isActive ? 1.0 : 0.5, child: widget.alpahbetsAvailable[i].toUpperCase().toText14( - color: !isActive ? AppColors.greyTextColor : AppColors.primaryRedColor + color: !isActive ? AppColors.greyTextColor : AppColors.primaryRedColor, isEnglishOnly: true ), ), ); diff --git a/lib/presentation/lab/lab_order_by_test.dart b/lib/presentation/lab/lab_order_by_test.dart index ac6f0f3b..5cb94320 100644 --- a/lib/presentation/lab/lab_order_by_test.dart +++ b/lib/presentation/lab/lab_order_by_test.dart @@ -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/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'dart:ui' as ui; class LabOrderByTest extends StatelessWidget { final VoidCallback onTap; @@ -36,7 +37,7 @@ class LabOrderByTest extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - '${tests!.description}'.toText16(isBold: true), + '${tests!.description}'.toText16(isBold: true, isEnglishOnly: true), SizedBox(height: 4.h), (appState.isArabic() ? tests!.testDescriptionAr : tests!.testDescriptionEn)!.toText12(fontWeight: FontWeight.w500), SizedBox(height: 8.h), @@ -44,7 +45,13 @@ class LabOrderByTest extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ 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, textColor: AppColors.textColor, ), diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index 900477e4..b3ef6cde 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -1 +1 @@ -import 'dart:async'; import 'dart:convert'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.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/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.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/appbar/collapsing_toolbar.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; import 'alphabeticScroll.dart'; import 'dart:ui' as ui; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); rangeViewModel = Provider.of(context); _appState = getIt(); return CollapsingListView( title: LocaleKeys.labResults.tr(context: context), search: () async { if (labProvider.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: labProvider.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; labProvider.filterLabReports(value); } } }, child: Consumer( builder: (context, labViewModel, child) { return SingleChildScrollView( physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.all(24.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, LocaleKeys.byVisit.tr()), CustomTabBarModel(null, LocaleKeys.byTest.tr()), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), ), ], ), if (activeIndex == 0) Padding( padding: EdgeInsets.symmetric(vertical: 10.h), child: Row( children: [ // CustomButton( // text: LocaleKeys.byClinic.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(true); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, // borderColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), // textColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12.f, // fontWeight: FontWeight.w500, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), // SizedBox(width: 8.h), // CustomButton( // text: LocaleKeys.byHospital.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(false); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, // borderColor: labViewModel.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, // textColor: labViewModel.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, // fontWeight: FontWeight.w500, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), ], ), ), SizedBox(height: 8.h), selectedFilterText!.isNotEmpty ? Column( children: [ AppCustomChipWidget( labelText: selectedFilterText!, backgroundColor: AppColors.alertColor, textColor: AppColors.whiteColor, deleteIcon: AppAssets.close_bottom_sheet_icon, deleteIconColor: AppColors.whiteColor, deleteIconHasColor: true, onDeleteTap: () { selectedFilterText = ""; labProvider.filterLabReports(""); }, ), SizedBox(height: 8.h), ], ) : SizedBox(), activeIndex == 0 ? // By Visit - show grouped view when available labViewModel.isLabOrdersLoading ? ListView.builder( shrinkWrap: true, physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: 5, itemBuilder: (context, index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ), ) : (labViewModel.patientLabOrders.isNotEmpty ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: labViewModel.patientLabOrders.length, itemBuilder: (context, index) { final group = labViewModel.patientLabOrders[index]; final isExpanded = expandedIndex == index; return AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, margin: EdgeInsets.symmetric(vertical: 8.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), child: InkWell( onTap: () { setState(() { expandedIndex = isExpanded ? null : index; }); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.all(16.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ AppCustomChipWidget( labelText: "${group.testDetails!.length} ${LocaleKeys.tests.tr(context: context)}", backgroundColor: AppColors.successColor.withOpacity(0.1), textColor: AppColors.successColor, ), AppCustomChipWidget( labelText: "${_appState.isArabic() ? group.isInOutPatientDescriptionN : group.isInOutPatientDescription}", backgroundColor: AppColors.warningColorYellow.withOpacity(0.1), textColor: AppColors.warningColorYellow, ) ], ), Icon(isExpanded ? Icons.expand_less : Icons.expand_more), ], ), SizedBox(height: 8.h), Row( mainAxisSize: MainAxisSize.min, children: [ Image.network( group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", width: 24.w, height: 24.h, fit: BoxFit.cover, ).circle(100), SizedBox(width: 8.h), Expanded(child: (group.doctorName ?? group.doctorNameEnglish ?? "").toString().toText14(weight: FontWeight.w500)), ], ), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ Directionality( textDirection: ui.TextDirection.ltr, child: AppCustomChipWidget( labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(group.orderDate ?? ""), false), isEnglishOnly: true, )), AppCustomChipWidget( labelText: (group.projectName ?? ""), ), AppCustomChipWidget( labelText: (group.clinicDescription ?? ""), ), ], ), ], ), ), AnimatedSwitcher( duration: Duration(milliseconds: 500), switchInCurve: Curves.easeIn, switchOutCurve: Curves.easeOut, transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: animation, child: SizeTransition( sizeFactor: animation, axisAlignment: 0.0, child: child, ), ); }, child: isExpanded ? Container( key: ValueKey(index), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 0.h), child: Column( children: [ ListView.separated( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemBuilder: (cxt, index) { PatientLabOrdersResponseModel order = group; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "• ${order.testDetails![index].description!}".toText14(weight: FontWeight.w500), SizedBox(height: 4.h), order.testDetails![index].testDescriptionEn!.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), // Row( // mainAxisSize: MainAxisSize.min, // children: [ // Image.network( // order.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", // width: 24.w, // height: 24.h, // fit: BoxFit.cover, // ).circle(100), // SizedBox(width: 8.h), // Expanded(child: (order.doctorName ?? order.doctorNameEnglish ?? "").toString().toText14(weight: FontWeight.w500)), // ], // ), // SizedBox(height: 8.h), // Wrap( // direction: Axis.horizontal, // spacing: 4.h, // runSpacing: 4.h, // children: [ // AppCustomChipWidget( // labelText: ("${LocaleKeys.orderNo.tr(context: context)}: ${order.orderNo!}"), isEnglishOnly: true, // ), // Directionality( // textDirection: ui.TextDirection.ltr, // child: AppCustomChipWidget( // labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false), // isEnglishOnly: true, // )), // AppCustomChipWidget( // labelText: labViewModel.isSortByClinic ? (order.projectName ?? "") : (order.clinicDescription ?? ""), // ), // ], // ), // // Row( // // children: [ // // CustomButton( // // text: ("Order No: ".needTranslation + order.orderNo!), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // fontWeight: FontWeight.w500, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // SizedBox(width: 8.h), // // CustomButton( // // text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // fontWeight: FontWeight.w500, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // ], // // ), // // SizedBox(height: 8.h), // // Row( // // children: [ // // CustomButton( // // text: model.isSortByClinic ? (order.clinicDescription ?? "") : (order.projectName ?? ""), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // fontWeight: FontWeight.w500, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // ], // // ), // SizedBox(height: 12.h), // Row( // children: [ // Expanded(flex: 2, child: SizedBox()), // // Expanded( // // flex: 1, // // child: Container( // // height: 40.h, // // width: 40.w, // // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( // // color: AppColors.textColor, // // borderRadius: 12, // // ), // // child: Padding( // // padding: EdgeInsets.all(12.h), // // child: Transform.flip( // // flipX: _appState.isArabic(), // // child: Utils.buildSvgWithAssets( // // icon: AppAssets.forward_arrow_icon_small, // // iconColor: AppColors.whiteColor, // // fit: BoxFit.contain, // // ), // // ), // // ), // // ).onPress(() { // // model.currentlySelectedPatientOrder = order; // // labProvider.getPatientLabResultByHospital(order); // // labProvider.getPatientSpecialResult(order); // // Navigator.of(context).push( // // CustomPageRoute(page: LabResultByClinic(labOrder: order)), // // ); // // }), // // ) // // Expanded( // flex: 2, // child: CustomButton( // icon: AppAssets.view_report_icon, // iconColor: AppColors.primaryRedColor, // iconSize: 16.h, // text: LocaleKeys.viewResults.tr(context: context), // onPressed: () { // labViewModel.currentlySelectedPatientOrder = order; // labProvider.getPatientLabResultByHospital(order); // labProvider.getPatientSpecialResult(order); // Navigator.of(context).push( // CustomPageRoute(page: LabResultByClinic(labOrder: order)), // ); // }, // backgroundColor: AppColors.secondaryLightRedColor, // borderColor: AppColors.secondaryLightRedColor, // textColor: AppColors.primaryRedColor, // fontSize: 14, // fontWeight: FontWeight.w500, // borderRadius: 12, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), // ) // ], // ), // SizedBox(height: 12.h), // Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), // SizedBox(height: 12.h), ], ).paddingOnly(top: 16, bottom: 16); }, separatorBuilder: (cxt, index) => Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), itemCount: group.testDetails!.length > 3 ? 3 : group.testDetails!.length), SizedBox(height: 16.h), CustomButton( text: "${LocaleKeys.viewResults.tr()} (${group.testDetails!.length})", onPressed: () { labProvider.currentlySelectedPatientOrder = group; labProvider.getPatientLabResultByHospital(group); labProvider.getPatientSpecialResult(group); Navigator.of(context).push( CustomPageRoute( page: LabResultByClinic(labOrder: group), ), ); }, backgroundColor: AppColors.infoColor.withAlpha(20), borderColor: AppColors.infoColor.withAlpha(0), textColor: AppColors.infoColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, fontWeight: FontWeight.w500, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, iconSize: 14.h, icon: AppAssets.view_report_icon, iconColor: AppColors.infoColor, ), SizedBox(height: 16.h), ], ), ) : SizedBox.shrink(), ), ], ), ), ), ), )); }, ) : Utils.getNoDataWidget(context, noDataText: LocaleKeys.noLabResults.tr(context: context))) : // By Test or other tabs keep existing behavior (labViewModel.isLabOrdersLoading) ? Column( children: List.generate( 5, (index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, )), ) : AlphabeticScroll( alpahbetsAvailable: labViewModel.indexedCharacterForUniqueTest, details: labViewModel.uniqueTestsList, labViewModel: labViewModel, rangeViewModel: rangeViewModel, appState: _appState, ) ], )); }, ), ); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file +import 'dart:async'; import 'dart:convert'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.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/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.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/appbar/collapsing_toolbar.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; import 'alphabeticScroll.dart'; import 'dart:ui' as ui; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); rangeViewModel = Provider.of(context); _appState = getIt(); return CollapsingListView( title: LocaleKeys.labResults.tr(context: context), search: () async { if (labProvider.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: labProvider.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; labProvider.filterLabReports(value); } } }, child: Consumer( builder: (context, labViewModel, child) { return SingleChildScrollView( physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.all(24.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, LocaleKeys.byVisit.tr()), CustomTabBarModel(null, LocaleKeys.byTest.tr()), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), ), ], ), if (activeIndex == 0) Padding( padding: EdgeInsets.symmetric(vertical: 10.h), child: Row( children: [ // CustomButton( // text: LocaleKeys.byClinic.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(true); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, // borderColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), // textColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12.f, // fontWeight: FontWeight.w500, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), // SizedBox(width: 8.h), // CustomButton( // text: LocaleKeys.byHospital.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(false); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, // borderColor: labViewModel.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, // textColor: labViewModel.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, // fontWeight: FontWeight.w500, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), ], ), ), SizedBox(height: 8.h), selectedFilterText!.isNotEmpty ? Column( children: [ AppCustomChipWidget( labelText: selectedFilterText!, backgroundColor: AppColors.alertColor, textColor: AppColors.whiteColor, deleteIcon: AppAssets.close_bottom_sheet_icon, deleteIconColor: AppColors.whiteColor, deleteIconHasColor: true, isEnglishOnly: true, onDeleteTap: () { selectedFilterText = ""; labProvider.filterLabReports(""); }, ), SizedBox(height: 8.h), ], ) : SizedBox(), activeIndex == 0 ? // By Visit - show grouped view when available labViewModel.isLabOrdersLoading ? ListView.builder( shrinkWrap: true, physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: 5, itemBuilder: (context, index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ), ) : (labViewModel.patientLabOrders.isNotEmpty ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: labViewModel.filteredLabOrders.length, itemBuilder: (context, index) { final group = labViewModel.filteredLabOrders[index]; final isExpanded = expandedIndex == index; return AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, margin: EdgeInsets.symmetric(vertical: 8.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), child: InkWell( onTap: () { setState(() { expandedIndex = isExpanded ? null : index; }); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.all(16.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ AppCustomChipWidget( labelText: "${group.testDetails!.length} ${LocaleKeys.tests.tr(context: context)}", backgroundColor: AppColors.successColor.withOpacity(0.1), textColor: AppColors.successColor, ), AppCustomChipWidget( labelText: "${_appState.isArabic() ? group.isInOutPatientDescriptionN : group.isInOutPatientDescription}", backgroundColor: AppColors.warningColorYellow.withOpacity(0.1), textColor: AppColors.warningColorYellow, ) ], ), Icon(isExpanded ? Icons.expand_less : Icons.expand_more), ], ), SizedBox(height: 8.h), Row( mainAxisSize: MainAxisSize.min, children: [ Image.network( group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", width: 24.w, height: 24.h, fit: BoxFit.cover, ).circle(100), SizedBox(width: 8.h), Expanded(child: (group.doctorName ?? group.doctorNameEnglish ?? "").toString().toText14(weight: FontWeight.w500)), ], ), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ Directionality( textDirection: ui.TextDirection.ltr, child: AppCustomChipWidget( labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(group.orderDate ?? ""), false), isEnglishOnly: true, )), AppCustomChipWidget( labelText: (group.projectName ?? ""), ), AppCustomChipWidget( labelText: (group.clinicDescription ?? ""), ), ], ), ], ), ), AnimatedSwitcher( duration: Duration(milliseconds: 500), switchInCurve: Curves.easeIn, switchOutCurve: Curves.easeOut, transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: animation, child: SizeTransition( sizeFactor: animation, axisAlignment: 0.0, child: child, ), ); }, child: isExpanded ? Container( key: ValueKey(index), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 0.h), child: Column( children: [ ListView.separated( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemBuilder: (cxt, index) { PatientLabOrdersResponseModel order = group; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "• ${order.testDetails![index].description!}".toText14(weight: FontWeight.w500), SizedBox(height: 4.h), order.testDetails![index].testDescriptionEn!.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), // Row( // mainAxisSize: MainAxisSize.min, // children: [ // Image.network( // order.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", // width: 24.w, // height: 24.h, // fit: BoxFit.cover, // ).circle(100), // SizedBox(width: 8.h), // Expanded(child: (order.doctorName ?? order.doctorNameEnglish ?? "").toString().toText14(weight: FontWeight.w500)), // ], // ), // SizedBox(height: 8.h), // Wrap( // direction: Axis.horizontal, // spacing: 4.h, // runSpacing: 4.h, // children: [ // AppCustomChipWidget( // labelText: ("${LocaleKeys.orderNo.tr(context: context)}: ${order.orderNo!}"), isEnglishOnly: true, // ), // Directionality( // textDirection: ui.TextDirection.ltr, // child: AppCustomChipWidget( // labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false), // isEnglishOnly: true, // )), // AppCustomChipWidget( // labelText: labViewModel.isSortByClinic ? (order.projectName ?? "") : (order.clinicDescription ?? ""), // ), // ], // ), // // Row( // // children: [ // // CustomButton( // // text: ("Order No: ".needTranslation + order.orderNo!), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // fontWeight: FontWeight.w500, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // SizedBox(width: 8.h), // // CustomButton( // // text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // fontWeight: FontWeight.w500, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // ], // // ), // // SizedBox(height: 8.h), // // Row( // // children: [ // // CustomButton( // // text: model.isSortByClinic ? (order.clinicDescription ?? "") : (order.projectName ?? ""), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // fontWeight: FontWeight.w500, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // ], // // ), // SizedBox(height: 12.h), // Row( // children: [ // Expanded(flex: 2, child: SizedBox()), // // Expanded( // // flex: 1, // // child: Container( // // height: 40.h, // // width: 40.w, // // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( // // color: AppColors.textColor, // // borderRadius: 12, // // ), // // child: Padding( // // padding: EdgeInsets.all(12.h), // // child: Transform.flip( // // flipX: _appState.isArabic(), // // child: Utils.buildSvgWithAssets( // // icon: AppAssets.forward_arrow_icon_small, // // iconColor: AppColors.whiteColor, // // fit: BoxFit.contain, // // ), // // ), // // ), // // ).onPress(() { // // model.currentlySelectedPatientOrder = order; // // labProvider.getPatientLabResultByHospital(order); // // labProvider.getPatientSpecialResult(order); // // Navigator.of(context).push( // // CustomPageRoute(page: LabResultByClinic(labOrder: order)), // // ); // // }), // // ) // // Expanded( // flex: 2, // child: CustomButton( // icon: AppAssets.view_report_icon, // iconColor: AppColors.primaryRedColor, // iconSize: 16.h, // text: LocaleKeys.viewResults.tr(context: context), // onPressed: () { // labViewModel.currentlySelectedPatientOrder = order; // labProvider.getPatientLabResultByHospital(order); // labProvider.getPatientSpecialResult(order); // Navigator.of(context).push( // CustomPageRoute(page: LabResultByClinic(labOrder: order)), // ); // }, // backgroundColor: AppColors.secondaryLightRedColor, // borderColor: AppColors.secondaryLightRedColor, // textColor: AppColors.primaryRedColor, // fontSize: 14, // fontWeight: FontWeight.w500, // borderRadius: 12, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), // ) // ], // ), // SizedBox(height: 12.h), // Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), // SizedBox(height: 12.h), ], ).paddingOnly(top: 16, bottom: 16); }, separatorBuilder: (cxt, index) => Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), itemCount: group.testDetails!.length > 3 ? 3 : group.testDetails!.length), SizedBox(height: 16.h), CustomButton( text: "${LocaleKeys.viewResults.tr()} (${group.testDetails!.length})", onPressed: () { labProvider.currentlySelectedPatientOrder = group; labProvider.getPatientLabResultByHospital(group); labProvider.getPatientSpecialResult(group); Navigator.of(context).push( CustomPageRoute( page: LabResultByClinic(labOrder: group), ), ); }, backgroundColor: AppColors.infoColor.withAlpha(20), borderColor: AppColors.infoColor.withAlpha(0), textColor: AppColors.infoColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, fontWeight: FontWeight.w500, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, iconSize: 14.h, icon: AppAssets.view_report_icon, iconColor: AppColors.infoColor, ), SizedBox(height: 16.h), ], ), ) : SizedBox.shrink(), ), ], ), ), ), ), )); }, ) : Utils.getNoDataWidget(context, noDataText: LocaleKeys.noLabResults.tr(context: context))) : // By Test or other tabs keep existing behavior (labViewModel.isLabOrdersLoading) ? Column( children: List.generate( 5, (index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, )), ) : AlphabeticScroll( alpahbetsAvailable: labViewModel.filteredIndexedCharacterForUniqueTest, details: labViewModel.filteredUniqueTestsList, labViewModel: labViewModel, rangeViewModel: rangeViewModel, appState: _appState, ) ], )); }, ), ); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file diff --git a/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart b/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart index 07b9fb65..6f68a6c1 100644 --- a/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart +++ b/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart @@ -27,7 +27,7 @@ class LabResultList extends StatelessWidget { shrinkWrap: true,itemCount: list.length,itemBuilder: (____, index) { var labItem = list[index]; 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, index: index, diff --git a/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart b/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart index 1a97d48d..ac2537e1 100644 --- a/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart +++ b/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart @@ -44,7 +44,7 @@ class LabOrderResultItem extends StatelessWidget { ), // (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), SizedBox(height: 12.h), Directionality( diff --git a/lib/presentation/lab/lab_results/lab_result_details.dart b/lib/presentation/lab/lab_results/lab_result_details.dart index ded1e693..aeef4240 100644 --- a/lib/presentation/lab/lab_results/lab_result_details.dart +++ b/lib/presentation/lab/lab_results/lab_result_details.dart @@ -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 '../../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight; +import 'dart:ui' as ui; class LabResultDetails extends StatelessWidget { final LabResult recentLabResult; @@ -63,6 +64,30 @@ class LabResultDetails extends StatelessWidget { LabNameAndStatus(context), getLabDescription(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( selector: (_, model) => model.labOrderResponseByAi, builder: (_, aiData, __) { @@ -170,7 +195,7 @@ class LabResultDetails extends StatelessWidget { ], ), 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( @@ -232,24 +257,25 @@ class LabResultDetails extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context), - style: TextStyle( - fontSize: 16.f, - fontFamily: 'Poppins', - fontWeight: FontWeight.w600, - color: AppColors.textColor, - ), - ), + (labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context)).toText16(isBold: true, color: AppColors.textColor), + // Text( + // labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context), + // style: TextStyle( + // fontSize: 16.f, + // fontFamily: 'Poppins', + // fontWeight: FontWeight.w600, + // color: AppColors.textColor, + // ), + // ), Row( spacing: 16.h, children: [ //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(() { - if (labmodel.shouldShowGraph) { - labmodel.alterGraphVisibility(); - } - }), + // Utils.buildSvgWithAssets(icon: labmodel.isGraphVisible ? AppAssets.ic_list : AppAssets.ic_graph, width: 24.h, height: 24.h).onPress(() { + // if (labmodel.shouldShowGraph) { + // labmodel.alterGraphVisibility(); + // } + // }), Utils.buildSvgWithAssets(icon: AppAssets.ic_date_filter, width: 24, height: 24).onPress(() { showCommonBottomSheetWithoutHeight( title: LocaleKeys.setTheDateRange.tr(context: context), @@ -311,6 +337,7 @@ class LabResultDetails extends StatelessWidget { minY: labmodel.minY, maxX: labmodel.filteredGraphValues.length.toDouble() - .75, horizontalInterval: .1, + isRTL: getIt.get().isArabic(), getDrawingHorizontalLine: (value) { value = double.parse(value.toStringAsFixed(1)); if (value == labmodel.highRefrenceValue || value == labmodel.lowRefenceValue) { @@ -392,10 +419,11 @@ class LabResultDetails extends StatelessWidget { Widget labHistoryList(LabViewModel labmodel) { 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( 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) { var data = labmodel.filteredGraphValues.reversed.toList()[index]; return LabHistoryItem( diff --git a/lib/presentation/lab/search_lab_report.dart b/lib/presentation/lab/search_lab_report.dart index d366f38f..be57150f 100644 --- a/lib/presentation/lab/search_lab_report.dart +++ b/lib/presentation/lab/search_lab_report.dart @@ -78,8 +78,9 @@ class _SearchLabResultsContentState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ TextInputWidget( - labelText: "Search lab results", - hintText: "Type test name", + fontFamily: "Poppins", + labelText: LocaleKeys.searchLabResults.tr(context: context), + hintText: "", controller: searchEditingController, isEnable: true, prefix: null, @@ -93,7 +94,7 @@ class _SearchLabResultsContentState extends State { ), SizedBox(height: ResponsiveExtension(20).h), 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: AppColors.textColor, fontWeight: FontWeight.w500, + isEnglishOnly: true ), ), ); diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 3e440723..25603bdc 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -546,7 +546,7 @@ class _MedicalFilePageState extends State { doctorImageURL: patientAppointmentHistoryResponseModel.doctorImageURL, doctorTitle: patientAppointmentHistoryResponseModel.doctorTitle, name: patientAppointmentHistoryResponseModel.doctorNameObj, - nationalityFlagURL: "https://hmgwebservices.com/Images/flag/SYR.png", + nationalityFlagURL: "", speciality: [], clinicName: patientAppointmentHistoryResponseModel.clinicName, projectName: patientAppointmentHistoryResponseModel.projectName, @@ -1566,7 +1566,7 @@ class _MedicalFilePageState extends State { label: LocaleKeys.weight.tr(context: context), value: vitalSign.weightKg?.toString() ?? '--', unit: 'kg', - status: vitalSign.weightKg != null ? "Normal" : null, + status: vitalSign.weightKg != null ? _getWeightStatus(vitalSign.bodyMassIndex) : null, onTap: onTap, ), ), @@ -1593,6 +1593,11 @@ class _MedicalFilePageState extends State { return VitalSignUiModel.bmiStatus(bmi); } + String? _getWeightStatus(dynamic bmi) { + if (bmi == null) return 'Normal'; + return VitalSignUiModel.bmiStatus(bmi); + } + String? _getBloodPressureStatus({dynamic systolic, dynamic diastolic}) { return VitalSignUiModel.bloodPressureStatus(systolic: systolic, diastolic: diastolic); } diff --git a/lib/presentation/medical_report/medical_reports_page.dart b/lib/presentation/medical_report/medical_reports_page.dart index fd3ba7f9..9dccdf79 100644 --- a/lib/presentation/medical_report/medical_reports_page.dart +++ b/lib/presentation/medical_report/medical_reports_page.dart @@ -419,6 +419,7 @@ class _MedicalReportsPageState extends State { callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, + isAutoDismiss: true ); } } diff --git a/lib/presentation/my_invoices/my_invoices_details_page.dart b/lib/presentation/my_invoices/my_invoices_details_page.dart index 0410d311..83cb885f 100644 --- a/lib/presentation/my_invoices/my_invoices_details_page.dart +++ b/lib/presentation/my_invoices/my_invoices_details_page.dart @@ -56,6 +56,7 @@ class _MyInvoicesDetailsPageState extends State { callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, + isAutoDismiss: true ); }, onError: (err) { diff --git a/lib/presentation/notifications/notifications_list_page.dart b/lib/presentation/notifications/notifications_list_page.dart index 6c344b2e..6c4e7279 100644 --- a/lib/presentation/notifications/notifications_list_page.dart +++ b/lib/presentation/notifications/notifications_list_page.dart @@ -17,144 +17,114 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:provider/provider.dart'; import 'package:intl/intl.dart'; -class NotificationsListPage extends StatelessWidget { +class NotificationsListPage extends StatefulWidget { const NotificationsListPage({super.key}); + @override + State createState() => _NotificationsListPageState(); +} + +class _NotificationsListPageState extends State { + 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 Widget build(BuildContext context) { return CollapsingListView( title: LocaleKeys.notification.tr(context: context), - child: SingleChildScrollView( - child: Consumer(builder: (context, notificationsVM, child) { - return Container( - margin: EdgeInsets.symmetric(vertical: 24.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.h, - hasShadow: false, - ), - child: ListView.builder( - itemCount: notificationsVM.isNotificationsLoading ? 4 : notificationsVM.notificationsList.length, - physics: NeverScrollableScrollPhysics(), - shrinkWrap: true, - padding: EdgeInsetsGeometry.zero, - itemBuilder: (context, index) { - return notificationsVM.isNotificationsLoading - ? LabResultItemView( + child: Consumer(builder: (context, notificationsVM, child) { + return Column( + children: [ + Container( + margin: EdgeInsets.symmetric(vertical: 24.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: false, + ), + child: ListView.builder( + itemCount: notificationsVM.isNotificationsLoading + ? 4 + : notificationsVM.notificationsList.length + (notificationsVM.hasMoreNotifications ? 1 : 0), + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: EdgeInsetsGeometry.zero, + itemBuilder: (context, index) { + if (notificationsVM.isNotificationsLoading) { + return LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, - ) - : AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 500), - child: SlideAnimation( - verticalOffset: 100.0, - child: FadeInAnimation( - child: GestureDetector( - onTap: () { - Navigator.push( - context, - 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, - ), + ); + } + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: GestureDetector( + onTap: () { + notificationsVM.markAsRead(notificationID: notificationsVM.notificationsList[index].id ?? 0); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => NotificationDetailsPage( + notification: notificationsVM.notificationsList[index], ), - SizedBox(width: 8.w), - // Red dot for unread notifications ONLY - if (notificationsVM.notificationsList[index].isRead == false) - Container( - width: 8.w, - height: 8.w, - decoration: BoxDecoration( - color: Colors.red, - shape: BoxShape.circle, - ), - ), - ], - ), - SizedBox(height: 12.h), - // First row: Time and Date chips with arrow - Row( + ), + ); + }, + child: AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Time chip with clock 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.access_time, size: 12.w, color: AppColors.textColor), - SizedBox(width: 4.w), - _formatTime(notificationsVM.notificationsList[index].isSentOn).toText10( - weight: FontWeight.w500, - color: AppColors.textColor, + 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), - // 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, + ), + SizedBox(width: 8.w), + // Red dot for unread notifications ONLY + if (notificationsVM.notificationsList[index].isRead == false) + Container( + width: 8.w, + height: 8.w, + decoration: BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + ), ), - ], - ), - ), - 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") - if (notificationsVM.notificationsList[index].messageType != null && - notificationsVM.notificationsList[index].messageType!.toLowerCase() == "image") - Padding( - padding: EdgeInsets.only(top: 8.h), - child: Row( + SizedBox(height: 12.h), + // First row: Time and Date chips with arrow + Row( children: [ + // Time chip with clock icon Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( @@ -164,31 +134,100 @@ class NotificationsListPage extends StatelessWidget { child: Row( mainAxisSize: MainAxisSize.min, 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), - 'Contains Image'.toText10( + _formatTime(notificationsVM.notificationsList[index].isSentOn).toText10( weight: FontWeight.w500, 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, + ), ], ), - ), - SizedBox(height: 16.h), - 1.divider, - ], + // Second row: Contains Image chip (if MessageType is "image") + if (notificationsVM.notificationsList[index].messageType != null && + 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), + ).paddingSymmetrical(24.w, 0.h), + SizedBox(height: 16.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), + ], + ); }), - ), ); } diff --git a/lib/presentation/parking/paking_page.dart b/lib/presentation/parking/paking_page.dart index db451b27..7673d15f 100644 --- a/lib/presentation/parking/paking_page.dart +++ b/lib/presentation/parking/paking_page.dart @@ -1,7 +1,10 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.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_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/widget_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; @@ -103,6 +106,18 @@ class _ParkingPageState extends State { child: Padding( padding: EdgeInsets.all(16.h), child: LocaleKeys.parkingDescription.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.textColor)), ).paddingOnly(top: 16, bottom: 16), + ClipRRect( + borderRadius: BorderRadius.circular(24.r), + child: Transform.flip( + flipX: getIt.get().isArabic(), + child: Image.asset( + AppAssets.carParkingService, + fit: BoxFit.fitHeight, + height: 480.h, + width: 520.w, + ), + ), + ), ], ), ), diff --git a/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart b/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart index d6a616ee..070132ad 100644 --- a/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart +++ b/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart @@ -135,6 +135,7 @@ class PrescriptionDeliveryOrderSummaryPage extends StatelessWidget { isCloseButtonVisible: true, isDismissible: false, isFullScreen: false, + isAutoDismiss: true ); }, onError: (err) { diff --git a/lib/presentation/profile_settings/widgets/preferred_language_widget.dart b/lib/presentation/profile_settings/widgets/preferred_language_widget.dart index dcd30360..9678349b 100644 --- a/lib/presentation/profile_settings/widgets/preferred_language_widget.dart +++ b/lib/presentation/profile_settings/widgets/preferred_language_widget.dart @@ -72,7 +72,7 @@ class _PreferredLanguageWidgetState extends State { callBackFunc: () async { Navigator.of(context).pop(); profileSettingsViewModel.getProfileSettings(); - }, isFullScreen: false); + }, isFullScreen: false, isAutoDismiss: true); }, onError: (error) { LoaderBottomSheet.hideLoader(); diff --git a/lib/presentation/profile_settings/widgets/update_email_widget.dart b/lib/presentation/profile_settings/widgets/update_email_widget.dart index cb844bcb..0b27dc82 100644 --- a/lib/presentation/profile_settings/widgets/update_email_widget.dart +++ b/lib/presentation/profile_settings/widgets/update_email_widget.dart @@ -96,7 +96,7 @@ class _UpdateEmailDialogState extends State { callBackFunc: () async { Navigator.of(context).pop(); profileSettingsViewModel!.getProfileSettings(); - }, isFullScreen: false); + }, isFullScreen: false, isAutoDismiss: true); }, onError: (error) { LoaderBottomSheet.hideLoader(); diff --git a/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart b/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart index f725bba1..71505dff 100644 --- a/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart +++ b/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart @@ -96,7 +96,7 @@ class _UpdateEmergencyContactDialogState extends State { final formKey = GlobalKey(); String note = ""; - int rating = 5; + int rating = 0; // ProjectViewModel? projectViewModel; AppointmentRatingViewModel? appointmentRatingViewModel; @@ -48,16 +49,16 @@ class _RateAppointmentDoctorState extends State { return Selector( selector: (_, vm) => vm.isRateClinic, - builder: (context, isRateClinic, child) => isRateClinic - ? RateAppointmentClinic(doctorNote: note, doctorRate: rating,) - : SizedBox( - height: sheetHeight, + builder: (context, isRateClinic, child) => + // isRateClinic ? RateAppointmentClinic(doctorNote: note, doctorRate: rating,) + // : + SizedBox( + height: sheetHeight, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Scrollable main content Expanded( - child: Padding( padding: const EdgeInsets.only(top: 0.0, left: 0, right: 0), child: Column( @@ -144,7 +145,7 @@ class _RateAppointmentDoctorState extends State { decoration: InputDecoration.collapsed( hintText: LocaleKeys.notes.tr(context: context), hintStyle: TextStyle( - fontSize: 16, + fontSize: 16.f, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.64, @@ -162,7 +163,6 @@ class _RateAppointmentDoctorState extends State { ), ), - // Bottom action buttons pinned to bottom SafeArea( top: false, @@ -172,7 +172,7 @@ class _RateAppointmentDoctorState extends State { children: [ Expanded( child: CustomButton( - text: "Later", + text: LocaleKeys.later.tr(context: context), backgroundColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA), textColor: Color(0xffED1C2B), @@ -184,12 +184,18 @@ class _RateAppointmentDoctorState extends State { SizedBox(width: 10), Expanded( child: CustomButton( - text: LocaleKeys.next.tr(context: context), - onPressed: () { + text: LocaleKeys.submit.tr(context: context), + onPressed: () async { // Set up clinic rating and show clinic rating view - appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),); - appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),); - appointmentRatingViewModel!.setClinicOrDoctor(true); + // appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),); + // appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),); + // 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(() {}); }, diff --git a/lib/presentation/rate_appointment/widget/doctor_row.dart b/lib/presentation/rate_appointment/widget/doctor_row.dart index 725ac7c3..d85518f0 100644 --- a/lib/presentation/rate_appointment/widget/doctor_row.dart +++ b/lib/presentation/rate_appointment/widget/doctor_row.dart @@ -36,32 +36,26 @@ class BuildDoctorRow extends StatelessWidget { SizedBox(height: 8.h), - isForClinic ? Wrap( direction: Axis.horizontal, spacing: 3.h, runSpacing: 4.h, children: [ AppCustomChipWidget( - labelText: appointmentDetails!.clinicName.toString(), - ), AppCustomChipWidget( icon: AppAssets.ic_date_filter, labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(appointmentDetails!.appointmentDate), false), - ), AppCustomChipWidget( icon: AppAssets.appointment_time_icon, labelText: appointmentDetails!.startTime.substring(0, appointmentDetails!.startTime.length - 3), - ), - ] ) : Wrap( direction: Axis.horizontal, @@ -69,26 +63,26 @@ class BuildDoctorRow extends StatelessWidget { runSpacing: 4.h, children: [ AppCustomChipWidget( - labelText: appointmentDetails!.projectName.toString(), - - ), AppCustomChipWidget( - labelText: 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), + ), + ], + ), + ], + ), ), - ), ], - )); + ), + ); } } \ No newline at end of file diff --git a/lib/presentation/symptoms_checker/possible_conditions_screen.dart b/lib/presentation/symptoms_checker/possible_conditions_screen.dart index 81980c66..79e28a05 100644 --- a/lib/presentation/symptoms_checker/possible_conditions_screen.dart +++ b/lib/presentation/symptoms_checker/possible_conditions_screen.dart @@ -37,6 +37,8 @@ class PossibleConditionsPage extends StatelessWidget { late BookAppointmentsViewModel bookAppointmentsViewModel; late AppointmentViaRegionViewmodel regionalViewModel; + late AppState appState; + Widget _buildLoadingShimmer() { return ListView.separated( shrinkWrap: true, @@ -223,6 +225,7 @@ class PossibleConditionsPage extends StatelessWidget { symptomsCheckerViewModel = context.read(); bookAppointmentsViewModel = context.read(); regionalViewModel = context.read(); + appState = getIt.get(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( @@ -248,7 +251,7 @@ class PossibleConditionsPage extends StatelessWidget { // Get selected symptoms names for display final symptoms = symptomsCheckerViewModel .getAllSelectedSymptoms() - .map((s) => s.commonName ?? s.name ?? '') + .map((s) => (appState.isArabic() ? s.commonNameAr ?? s.nameAr ?? '' : s.commonName ?? s.name ?? '')) .where((name) => name.isNotEmpty) .take(3) .toList(); @@ -376,7 +379,6 @@ class PossibleConditionsPage extends StatelessWidget { }, ); } else { - final appState = getIt.get(); appState.resetLocation(); regionVM.setSortByLocation(false); _refreshHospitalListAfterApi(regionVM); diff --git a/lib/presentation/todo_section/ancillary_order_payment_page.dart b/lib/presentation/todo_section/ancillary_order_payment_page.dart index 9d75c769..75c76dc8 100644 --- a/lib/presentation/todo_section/ancillary_order_payment_page.dart +++ b/lib/presentation/todo_section/ancillary_order_payment_page.dart @@ -491,7 +491,7 @@ class _AncillaryOrderPaymentPageState extends State { child: Utils.getSuccessWidget(loadingText: "${LocaleKeys.paymentCompletedSuccessfully.tr(context: context)}, ${LocaleKeys.hereIsYourInvoiceNumber.tr(context: context)}$invoiceNo"), isCloseButtonVisible: true, isDismissible: true, - isFullScreen: false, callBackFunc: () { + isFullScreen: false, isAutoDismiss: true, callBackFunc: () { // Pop until we reach the LandingPage todoSectionViewModel.setIsAncillaryOrdersNeedReloading(true); Navigator.pushAndRemoveUntil( diff --git a/lib/presentation/vital_sign/vital_sign_details_page.dart b/lib/presentation/vital_sign/vital_sign_details_page.dart index ca6e09a5..587a910f 100644 --- a/lib/presentation/vital_sign/vital_sign_details_page.dart +++ b/lib/presentation/vital_sign/vital_sign_details_page.dart @@ -89,6 +89,21 @@ class _VitalSignDetailsPageState extends State { ), _whatIsThisResultCard(context), _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), ); @@ -264,35 +279,27 @@ class _VitalSignDetailsPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - _isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context), - style: TextStyle( - fontSize: 16, - fontFamily: 'Poppins', - fontWeight: FontWeight.w600, - color: AppColors.textColor, - ), - ), + LocaleKeys.historyFlowchart.tr(context: context).toText16(isBold: true, color: AppColors.textColor), Row( mainAxisSize: MainAxisSize.min, children: [ - Container( - width: 24.h, - height: 24.h, - alignment: Alignment.center, - child: InkWell( - onTap: () { - setState(() { - _isGraphVisible = !_isGraphVisible; - }); - }, - child: Utils.buildSvgWithAssets( - icon: _isGraphVisible ? AppAssets.ic_list : AppAssets.ic_graph, - width: 24.h, - height: 24.h, - ), - ), - ), + // Container( + // width: 24.h, + // height: 24.h, + // alignment: Alignment.center, + // child: InkWell( + // onTap: () { + // setState(() { + // _isGraphVisible = !_isGraphVisible; + // }); + // }, + // child: Utils.buildSvgWithAssets( + // icon: _isGraphVisible ? AppAssets.ic_list : AppAssets.ic_graph, + // width: 24.h, + // height: 24.h, + // ), + // ), + // ), // SizedBox(width: 16.h), // Container( // width: 24.h, @@ -663,7 +670,8 @@ class _VitalSignDetailsPageState extends State { case VitalSignMetric.height: return null; case VitalSignMetric.weight: - return latest.weightKg != null ? 'Normal' : null; + // return latest.weightKg != null ? 'Normal' : null; + return VitalSignUiModel.bmiStatus(latest.bodyMassIndex); case VitalSignMetric.temperature: return null; case VitalSignMetric.heartRate: diff --git a/lib/presentation/vital_sign/vital_sign_page.dart b/lib/presentation/vital_sign/vital_sign_page.dart index 82c64686..4fa90233 100644 --- a/lib/presentation/vital_sign/vital_sign_page.dart +++ b/lib/presentation/vital_sign/vital_sign_page.dart @@ -138,11 +138,13 @@ class _VitalSignPageState extends State { label: LocaleKeys.weight.tr(context: context), value: latestVitalSign?.weightKg?.toString() ?? '--', unit: 'kg', - status: (latestVitalSign?.weightKg != null) ? 'Normal' : null, + status: (latestVitalSign?.weightKg != null) + ? VitalSignUiModel.bmiStatus(latestVitalSign?.bodyMassIndex) + : null, onTap: () => _openDetails( VitalSignDetailsArgs( metric: VitalSignMetric.weight, - title: LocaleKeys.height.tr(context: context), + title: LocaleKeys.weight.tr(context: context), icon: AppAssets.weightVital, unit: 'kg', ), diff --git a/lib/services/dialog_service.dart b/lib/services/dialog_service.dart index d2ba2723..ac7acf9e 100644 --- a/lib/services/dialog_service.dart +++ b/lib/services/dialog_service.dart @@ -23,7 +23,7 @@ abstract class DialogService { Future showExceptionBottomSheet({required String message, required Function() onOkPressed, Function()? onCancelPressed}); - Future showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, required Function() onOkPressed, Function()? onCancelPressed}); + Future showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed}); Future showSuccessBottomSheetWithoutH({String? label, required String message, required Function() onOkPressed, Function()? onCancelPressed}); @@ -119,7 +119,7 @@ class DialogServiceImp implements DialogService { } @override - Future showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, required Function() onOkPressed, Function()? onCancelPressed}) async { + Future showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed}) async { final context = navigationService.navigatorKey.currentContext; if (context == null) return; showCommonBottomSheetWithoutHeight( @@ -132,6 +132,7 @@ class DialogServiceImp implements DialogService { cancelLabel: cancelLabel, onOkPressed: onOkPressed, onCancelPressed: onCancelPressed, + isConfirmButton: isConfirmButton ), 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( children: [ (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), onPressed: onOkPressed, backgroundColor: AppColors.bgGreenColor, - borderColor: AppColors.bgGreenColor, + borderColor: AppColors.successColor, textColor: Colors.white, icon: AppAssets.confirm, ), @@ -284,8 +285,9 @@ Widget exceptionBottomSheetWidget({required BuildContext context, required Strin context.pop(); } : onCancelPressed, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedBorderColor, + // backgroundColor: AppColors.primaryRedColor, + backgroundColor: isConfirmButton ? AppColors.successColor : AppColors.primaryRedColor, + borderColor: isConfirmButton ? AppColors.successColor : AppColors.primaryRedColor, textColor: Colors.white, icon: AppAssets.confirm, ), diff --git a/lib/widgets/app_language_change.dart b/lib/widgets/app_language_change.dart index 1200c53b..4a589ae7 100644 --- a/lib/widgets/app_language_change.dart +++ b/lib/widgets/app_language_change.dart @@ -57,9 +57,9 @@ class _AppLanguageChangeState extends State { decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), child: Column( children: [ - languageItem("English", "en"), - 1.divider, languageItem("العربية", "ar"), + 1.divider, + languageItem("English", "en"), ], ), ), diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart index bc52c0cc..9b5fa025 100644 --- a/lib/widgets/common_bottom_sheet.dart +++ b/lib/widgets/common_bottom_sheet.dart @@ -116,6 +116,7 @@ void showCommonBottomSheet(BuildContext context, Function(String?)? callBackFunc, String? title, required double height, + bool isAutoDismiss = false, bool isCloseButtonVisible = true, bool isFullScreen = true, bool isDismissible = true, @@ -131,6 +132,14 @@ void showCommonBottomSheet(BuildContext context, isDismissible: isDismissible, backgroundColor: isSuccessDialog ? AppColors.whiteColor : AppColors.scaffoldBgColor, builder: (BuildContext context) { + if (isAutoDismiss) { + Future.delayed(Duration(seconds: 2), () { + Navigator.of(context).pop(); + if (callBackFunc != null) { + callBackFunc(null); + } + }); + } return Container( height: height, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.scaffoldBgColor, borderRadius: 24.h), @@ -211,9 +220,11 @@ void showCommonBottomSheetWithoutHeight( required Widget child, VoidCallback? callBackFunc, String title = "", + bool isConfirmButton = false, bool isCloseButtonVisible = true, bool isFullScreen = true, bool isDismissible = true, + bool isAutoDismiss = false, Widget? titleWidget, bool useSafeArea = false, bool hasBottomPadding = true, @@ -237,6 +248,14 @@ void showCommonBottomSheetWithoutHeight( backgroundColor: resolvedBgColor, useSafeArea: useSafeArea, builder: (BuildContext context) { + if (isAutoDismiss) { + Future.delayed(Duration(seconds: 2), () { + Navigator.of(context).pop(); + if (callBackFunc != null) { + callBackFunc(); + } + }); + } return SafeArea( top: false, left: false, diff --git a/lib/widgets/custom_tab_bar.dart b/lib/widgets/custom_tab_bar.dart index b93519ca..79741add 100644 --- a/lib/widgets/custom_tab_bar.dart +++ b/lib/widgets/custom_tab_bar.dart @@ -48,6 +48,14 @@ class CustomTabBarState extends State { super.initState(); } + @override + void didUpdateWidget(covariant CustomTabBar oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.initialIndex != widget.initialIndex) { + selectedIndex = widget.initialIndex; + } + } + @override void dispose() { super.dispose(); diff --git a/lib/widgets/graph/custom_graph.dart b/lib/widgets/graph/custom_graph.dart index 3b0629f0..c655ec0d 100644 --- a/lib/widgets/graph/custom_graph.dart +++ b/lib/widgets/graph/custom_graph.dart @@ -31,6 +31,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; /// - [showBottomTitleDates]: Whether to show bottom axis labels. /// - [isFullScreeGraph]: Whether the graph is fullscreen. /// - [makeGraphBasedOnActualValue]: Use `actualValue` for plotting. +/// - [isRTL]: Whether the graph should render in right-to-left direction. /// /// Example usage: /// ```dart @@ -70,6 +71,7 @@ class CustomGraph extends StatelessWidget { final bool showLinePoints; final double? cutOffY; 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 final Widget Function(double) leftLabelFormatter; @@ -113,7 +115,8 @@ class CustomGraph extends StatelessWidget { this.showShadow = false, this.showLinePoints = false, this.cutOffY = 0, - this.rangeAnnotations}); + this.rangeAnnotations, + this.isRTL = false}); @override Widget build(BuildContext context) { @@ -122,6 +125,12 @@ class CustomGraph extends StatelessWidget { final resolvedGraphShadowColor = graphShadowColor ?? 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( color: AppColors.whiteColor, child: SizedBox( @@ -176,10 +185,10 @@ class CustomGraph extends StatelessWidget { // Show tooltip for each touched line return touchedSpots.map((spot) { // Determine which dataset this spot belongs to - final isSecondary = secondaryDataPoints != null && spot.barIndex == 1; + final isSecondary = displaySecondaryDataPoints != null && spot.barIndex == 1; final dataPoint = isSecondary - ? secondaryDataPoints![spot.x.toInt()] - : dataPoints[spot.x.toInt()]; + ? displaySecondaryDataPoints[spot.x.toInt()] + : displayDataPoints[spot.x.toInt()]; return LineTooltipItem( '${dataPoint.actualValue} ${dataPoint.unitOfMeasurement ?? ""} - ${dataPoint.displayTime}', @@ -192,9 +201,19 @@ class CustomGraph extends StatelessWidget { titlesData: FlTitlesData( leftTitles: AxisTitles( 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, - interval: leftLabelInterval ?? .1, // Let fl_chart handle it + interval: leftLabelInterval ?? .1, getTitlesWidget: (value, _) { return leftLabelFormatter(value); }, @@ -206,30 +225,28 @@ class CustomGraph extends StatelessWidget { showTitles: showBottomTitleDates, reservedSize: bottomLabelReservedSize ?? 20, getTitlesWidget: (value, _) { - return bottomLabelFormatter(value, dataPoints); + return bottomLabelFormatter(value, displayDataPoints); }, interval: 1, // ensures 1:1 mapping with spots ), ), topTitles: AxisTitles(), - rightTitles: AxisTitles(), ), borderData: FlBorderData( show: true, - border: const Border( + border: Border( bottom: BorderSide.none, - left: BorderSide(color: Colors.grey, width: .5), - right: BorderSide.none, + left: isRTL ? BorderSide.none : const BorderSide(color: Colors.grey, width: .5), + right: isRTL ? const BorderSide(color: Colors.grey, width: .5) : BorderSide.none, top: BorderSide.none, ), ), - lineBarsData: _buildColoredLineSegments(dataPoints, showLinePoints, resolvedGraphColor, resolvedGraphShadowColor), + lineBarsData: _buildColoredLineSegments(displayDataPoints, showLinePoints, resolvedGraphColor, resolvedGraphShadowColor, + secondaryPoints: displaySecondaryDataPoints), gridData: FlGridData( show: showGridLines ?? true, drawVerticalLine: false, horizontalInterval: horizontalInterval, - // checkToShowHorizontalLine: (value) => - // value >= 0 && value <= 100, getDrawingHorizontalLine: getDrawingHorizontalLine ?? (value) { return FlLine( @@ -245,7 +262,8 @@ class CustomGraph extends StatelessWidget { ); } - List _buildColoredLineSegments(List dataPoints, bool showLinePoints, Color resolvedGraphColor, Color resolvedGraphShadowColor) { + List _buildColoredLineSegments(List dataPoints, bool showLinePoints, Color resolvedGraphColor, Color resolvedGraphShadowColor, + {List? secondaryPoints}) { final List allSpots = dataPoints.asMap().entries.map((entry) { double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.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) - if (secondaryDataPoints != null && secondaryDataPoints!.isNotEmpty) { - final List secondarySpots = secondaryDataPoints!.asMap().entries.map((entry) { + if (secondaryPoints != null && secondaryPoints.isNotEmpty) { + final List secondarySpots = secondaryPoints.asMap().entries.map((entry) { double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value; return FlSpot(entry.key.toDouble(), value); }).toList(); diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index 25dd4580..e162c5ea 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -289,15 +289,7 @@ class TextInputWidget extends StatelessWidget { } Widget _buildLabelText(Color? labelColor) { - return Text( - labelText, - style: TextStyle( - fontSize: 12.f, - fontWeight: FontWeight.w500, - color: labelColor ?? AppColors.inputLabelTextColor, - letterSpacing: -0, - ), - ); + return labelText.toText12(fontWeight: FontWeight.w500, color: labelColor ?? AppColors.inputLabelTextColor); } Widget _buildTextField(BuildContext context) { diff --git a/lib/widgets/loader/bottomsheet_loader.dart b/lib/widgets/loader/bottomsheet_loader.dart index bff293d0..306d7ce4 100644 --- a/lib/widgets/loader/bottomsheet_loader.dart +++ b/lib/widgets/loader/bottomsheet_loader.dart @@ -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/utils/size_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/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -13,7 +15,7 @@ class LoaderBottomSheet { static final NavigationService _navService = GetIt.I(); static bool _isVisible = false; - static void showLoader({String? loadingText}) { + static void showLoader({String? loadingText, bool showCloseButton = false, Function? onCloseTap}) { if (_isVisible) return; _isVisible = true; @@ -32,7 +34,31 @@ class LoaderBottomSheet { color: AppColors.whiteColor, 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), ).paddingSymmetrical(24.w, 0), ); diff --git a/lib/widgets/routes/custom_page_route.dart b/lib/widgets/routes/custom_page_route.dart index b89d7102..7cddf18d 100644 --- a/lib/widgets/routes/custom_page_route.dart +++ b/lib/widgets/routes/custom_page_route.dart @@ -10,7 +10,6 @@ class CustomPageRoute extends PageRouteBuilder { CustomPageRoute({required this.page, this.direction = AxisDirection.right, this.fullScreenDialog = false, super.settings}) : super( - transitionDuration: const Duration(milliseconds: 1500), reverseTransitionDuration: const Duration(milliseconds: 500), fullscreenDialog: fullScreenDialog,