From cfcbaa85a305f4e52e52cfe0ca76201836f6ecd6 Mon Sep 17 00:00:00 2001 From: tahaalam Date: Wed, 15 Apr 2026 20:18:56 +0300 Subject: [PATCH 1/2] LANGUAGE CHANGES --- assets/images/svg/document.svg | 5 + assets/images/svg/report.svg | 4 + assets/images/svg/sucess.svg | 3 + assets/langs/ar-SA.json | 30 +- assets/langs/en-US.json | 35 +- lib/core/app_assets.dart | 3 + lib/core/enums.dart | 32 +- lib/generated/locale_keys.g.dart | 17 + .../blood_donation/blood_donation_page.dart | 2 +- .../widgets/select_gender_widget.dart | 2 +- .../book_appointment_page.dart | 2 +- .../book_appointment/select_clinic_page.dart | 2 +- .../comprehensive_checkup_page.dart | 2 +- .../emergency_services_page.dart | 2 +- .../emergency_services/nearest_er_page.dart | 2 +- .../health_calculators_page.dart | 17 +- .../widgets/bf.dart | 348 ++++++------------ .../widgets/blood_sugar.dart | 4 +- .../widgets/bmr.dart | 83 +++-- .../widgets/calories.dart | 241 ++++++------ .../widgets/crabs.dart | 2 +- .../widgets/ibw.dart | 66 ++-- .../medical_file/medical_file_page.dart | 135 ++++--- .../widgets/medical_report_card.dart | 101 +++++ .../prescriptions/prescription_item_view.dart | 2 +- 25 files changed, 621 insertions(+), 521 deletions(-) create mode 100644 assets/images/svg/document.svg create mode 100644 assets/images/svg/report.svg create mode 100644 assets/images/svg/sucess.svg create mode 100644 lib/presentation/medical_file/widgets/medical_report_card.dart diff --git a/assets/images/svg/document.svg b/assets/images/svg/document.svg new file mode 100644 index 00000000..5073ead5 --- /dev/null +++ b/assets/images/svg/document.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/report.svg b/assets/images/svg/report.svg new file mode 100644 index 00000000..b5ca54b1 --- /dev/null +++ b/assets/images/svg/report.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/sucess.svg b/assets/images/svg/sucess.svg new file mode 100644 index 00000000..6df93d5e --- /dev/null +++ b/assets/images/svg/sucess.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index a8df3819..b95b6709 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -680,8 +680,8 @@ "noOffersAvailable": "لا توجد عروض متاحة!", "accessibility": "وضع الوصول", "orderStatus": "حالة الطلب", - "findUs": "اعثر علينا", - "liveChat": "دردشة مباشرة", + "findUs": "فروعنا", + "liveChat": "محادثة مباشرة", "service": "خدمة", "hmgServiceLabel": "خدمة مجموعة الحبيب الطبية", "healthWeatherIndicators": "مؤشرات الطقس الصحية", @@ -1049,9 +1049,9 @@ "noServicesAvailable": "لا توجد خدمات متاحة", "selectAService": "اختر خدمة", "comprehensiveCheckup": "الفحص الشامل", - "viewNearestHMGLocations": "عرض أقرب مواقع مجموعة الحبيب الطبية", + "viewNearestHMGLocations": "عرض أقرب فرع", "provideFeedbackOnServices": "قدم ملاحظاتك على خدماتنا", - "liveChatWithHMG": "خيار الدردشة المباشرة مع مجموعة الحبيب الطبية", + "liveChatWithHMG": "حدث مباشرة مع الموظف", "send": "إرسال", "status": "الحالة", "sendingFeedback": "جاري إرسال الملاحظات...", @@ -1160,11 +1160,18 @@ "recharge": "اضافة رصيد", "activityLevel": "مستوى النشاط", "selectActivityLevel": "حدد مستوى النشاط", + "almostInactive": "غير نشط تقريبًا (بدون تمارين)", + "lightlyActive": "نشاط خفيف", + "lightlyActive13Days": "نشاط خفيف (١-٣) أيام في الأسبوع", + "superActive": "نشاط مكثف (تمارين شاقة جدًا)", "caloriesPerDay": "السعرات الحرارية في اليوم الواحد", "dietType": "نوع النظام الغذائي", "selectDietType": "حدد نوع النظام الغذائي", "bodyFrameSize": "حجم إطار الجسم", "selectBodyFrameSize": "حدد حجم إطار الجسم", + "bodyFrameSmall": "صغير (الأصابع متداخلة)", + "bodyFrameMedium": "متوسط (الأصابع متلامسة)", + "bodyFrameLarge": "كبير (الأصابع لا تتلامس)", "averageCycleLength": "متوسط طول الدورة الشهرية (عادةً 28 يومًا)", "averageLutealPhase": "متوسط طول المرحلة الأصفرية (عادةً 14 يومًا)", "convert": "تحويل الوحدات", @@ -1255,7 +1262,7 @@ "virtualTour": "جولة افتراضية", "carParking": "موقف السيارات", "latestNews": "آخر الأخبار", - "hmgContact": "اتصل بمجموعة الحبيب الطبية", + "hmgContact": "تواصل معنا", "durationCannotExceed90": "لا يجوز أن تتجاوز المدة 90 دقيقة", "unexpectedError": "حدث خطأ غير متوقع", "gettingAmbulanceTransportOption": "جاري الحصول على خيارات نقل الإسعاف", @@ -1334,6 +1341,7 @@ "askYourDoctor": "اسأل طبيبك", "internetPairing": "الاقتران بالإنترنت", "requested": "مطلوب", + "readyToDownload": "جاهز للتحميل", "youDontHaveAnyMedicalReportsYet": "ليس لديك أي تقارير طبية بعد.", "requestMedicalReport": "طلب تقرير طبي", "youDoNotHaveAnyAppointmentsToRequestMedicalReport": "ليس لديك أي مواعيد لطلب تقرير طبي.", @@ -1705,7 +1713,7 @@ "enterAmount": "يرجى إدخال المبلغ للمتابعة.", "selectHospitalContinue": "يرجى اختيار المستشفى للمتابعة.", "year": "سنة", - "notifyPrescription": "أبلغني قبل وقت الاستهلاك", + "notifyPrescription": "تنبيه قبل موعد الجرعة", "doctorRatingAppointment": "طبيب\nتصنيف", "ratingSubmitted": "تم إرسال التقييم", "rateTheDoctor": "يرجى تقييم الطبيب", @@ -1768,6 +1776,14 @@ "locationServicesDisabled": "حالة الطقس غير متوفرة لتعطيل خدمات الموقع. يرجى تفعيل خدمات الموقع لتتمكن من رؤية معلومات الطقس.", "selectDateTimeKey": "اختيار", "medicalKey": "الطبية", - "convertBloodcholesterolInfo":"حول قيمة كوليسترول الدم بين ملجم/ديسيلتر و مليمول/لتر" + "convertBloodcholesterolInfo":"حول قيمة كوليسترول الدم بين ملجم/ديسيلتر و مليمول/لتر", + "carbsAlert": "حساب نسب المغذيات (الكربوهيدرات ،البروتين والدهون) بالسعرات الحرارية والقرامات وفقا لنسب محددة مسبقا", + "bodyFatAlert": "يحسب بناءً على الحجم", + "ibwAlert": "حساب الوزن المثالي بناءً على الطول، الوزن، وبنية الجسم", + "glucoseAlert":"تغيير وحدات القياس بين مليجرام لكل ديسيلتر و ملي مول لكل لتر", + "selectBloodGroup": "اختر الفصيلة", + "routePrescription": "طريقة الاستخدام", + "noNearestER": "لا يوجد طوارئ قريب منك", + "loginToUseServiceLiveCare": "تحتاج إلى تسجيل الدخول لاستخدام هذه الخدمة" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index ee1c1d01..d0900bd8 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -944,7 +944,7 @@ "doctorWillContact": "Doctor will contact you", "specialisedDoctorWillContactYou": "A specialized doctor will contact you and will be able to view your medical history", "freeMedicineDelivery": "Free medicine delivery", - "offersFreeMedicineDelivery": "Offers free medicine delivery for the LiveCare appointment", + "offersFreeMedicineDelivery": "Receive free medicine delivery with your LiveCare appointment.", "dentalChiefComplaints": "Dental Chief Complaints", "viewAvailableAppointments": "View available appointments", "doctorProfile": "Doctor Profile", @@ -1136,14 +1136,14 @@ "allFacilities": "All Facilities", "selectLocation": "Select Location", "pleaseSelectTheLocation": "Please select the location", - "viewLocationGoogleMaps": "View Location on Google Maps", + "viewLocationGoogleMaps": "Get Directions on Google Maps", "callAmbulance": "Call Ambulance", - "requestAmbulanceInEmergency": "Request an ambulance in emergency from home or hospital", + "requestAmbulanceInEmergency": "Request an ambulance for immediate assistance in cases of emergency.", "confirmation": "Confirmation", "areYouSureYouWantToCallAmbulance": "Are you sure you want to call an ambulance?", - "getDetailsOfNearestBranch": "Get the details of nearest branch including directions", + "getDetailsOfNearestBranch": "Find the nearest ER and get directions.", "areYouSureYouWantToCallRRT": "Are you sure you want to call Rapid Response Team (RRT)?", - "priorERCheckInToSkipLine": "Prior ER Check-In to skip the line & payment at the reception.", + "priorERCheckInToSkipLine": "Check in before you arrive to the ER to skip the line & payment at reception.", "areYouSureYouWantToMakeERCheckIn": "Are you sure you want to request an online ER check-in?", "checkingYourERAppointmentStatus": "Please Wait...", "transportOptions": "Transport Options", @@ -1151,11 +1151,18 @@ "recharge": "Add Funds", "activityLevel": "Activity Level", "selectActivityLevel": "Select Activity Level", + "almostInactive": "Almost Inactive (no exercise)", + "lightlyActive": "Lightly active", + "lightlyActive13Days": "Lightly active (1-3) days per week", + "superActive": "Super active (very hard exercise)", "caloriesPerDay": "Calories Per Day", "dietType": "Diet Type", "selectDietType": "Select Diet Type", "bodyFrameSize": "Body Frame Size", "selectBodyFrameSize": "Select Body Frame Size", + "bodyFrameSmall": "Small (fingers overlaps)", + "bodyFrameMedium": "Medium (fingers touch)", + "bodyFrameLarge": "Large (fingers don't touch)", "averageCycleLength": "Average Cycle Length (Usually 28 days)", "averageLutealPhase": "Average Luteal Phase Length(Usually 14 days)", "convert": "Convert", @@ -1323,6 +1330,7 @@ "askYourDoctor": "Ask Your Doctor", "internetPairing": "Internet Pairing", "requested": "Requested", + "readyToDownload": "Ready to download", "youDontHaveAnyMedicalReportsYet": "You don't have any medical reports yet.", "requestMedicalReport": "Request medical report", "youDoNotHaveAnyAppointmentsToRequestMedicalReport": "You do not have any appointments to request a medical report.", @@ -1662,8 +1670,8 @@ "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.", + "emergencyServiceHeader": "Emergency Care, Faster than Ever.", + "emergencyServiceDescription": "Facing a medical emergency? Our ambulances are on standby 24/7 for your immediate assistance.", "homeHealthCareServiceHeader": "Premium care, delivered right to your doorstep", "homeHealthCareServiceDescription": "We provide the highest degree of patient care at home in a value-oriented, efficient, and professional manner.", "profileOnlyText": "Profile", @@ -1705,7 +1713,7 @@ "overWeight": "OverWeight", "underWeight": "UnderWeight", "nearestGate": "Nearest Gate", - "RRTDDetails": "The Rapid response team provides a comprehensive medical service for all sorts of urgent and stable cases of all ages, including adult and paediatric patients.", + "RRTDDetails": "Comprehensive medical services for stable and urgent cases, available for all ages.", "fromHospitalSubtitle": "Transfer from the hospital back to your home", "normalMapType": "Default", "satelliteMapType": "Satellite", @@ -1758,7 +1766,16 @@ "selectDateTimeKey": "Select", "medicalKey": "Medical" , - "convertBloodcholesterolInfo": "Convert blood cholesterol values between mg/dL and mmol/L. " + "convertBloodcholesterolInfo": "Convert blood cholesterol values between mg/dL and mmol/L. ", + "carbsAlert":"Calculate carbohydrate based protein and fat ration in calories and grams according to a pre set ratio.", + "bodyFatAlert": "Estimate the total body fat based on the size.", + "ibwAlert": "Calculates the ideal body weight based on height, weight and body size.", + "glucoseAlert":"Convert blood glucose values between mg/dL and mmol/L. (1 mmol/L ≈ 18.0182 mg/dL)", + "selectBloodGroup": "Select Blood Group", + "routePrescription": "Route", + "noNearestER": "No nearest Er Around you", + "loginToUseServiceLiveCare": "Log In to Use This Service" + } diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index c3811a94..7800d244 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -17,6 +17,9 @@ class AppAssets { static const String email = '$svgBasePath/email.svg'; static const String globe = '$svgBasePath/globe.svg'; static const String globeOther = '$svgBasePath/globe_other.svg'; + static const String document = '$svgBasePath/document.svg'; + static const String report = '$svgBasePath/report.svg'; + static const String sucess = '$svgBasePath/sucess.svg'; // static const String globeOther = '$svgBasePath/globe_other.svg'; static const String cancel = '$svgBasePath/cancel.svg'; diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 2d42a2ae..4ac919e7 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -5,8 +5,11 @@ // unverified, // } +import 'package:easy_localization/easy_localization.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/generated/locale_keys.g.dart'; enum AuthMethodTypesEnum { sms, whatsApp, fingerPrint, faceID, moreOptions } @@ -20,6 +23,17 @@ enum SelectionTypeEnum { dropdown, calendar, search, time } enum GenderTypeEnum { male, female } +extension GenderTypeEnumExtension on GenderTypeEnum { + String get displayName { + switch (this) { + case GenderTypeEnum.male: + return LocaleKeys.malE.tr(); + case GenderTypeEnum.female: + return LocaleKeys.femaleGender.tr(); + } + } +} + enum MaritalStatusTypeEnum { single, married, divorced, widowed } enum ChipTypeEnum { success, error, alert, info, warning, lightBg, primaryRed } @@ -68,7 +82,7 @@ extension HealthCalculatorExtenshion on HealthCalculatorsTypeEnum { case HealthCalculatorsTypeEnum.bodyFat: return isArabic ? "دهون الجسم" : "Body Fat Calculator"; case HealthCalculatorsTypeEnum.crabsProteinFat: - return isArabic ? "حاسبة البروتين والدهون في سرطان البحر" : "Crabs Protein & Fat Calculator"; + return isArabic ? "حاسبة المغذيات" : "Crabs Protein & Fat Calculator"; case HealthCalculatorsTypeEnum.ovulation: return isArabic ? "فترة الإباضة" : "Ovulation Period"; case HealthCalculatorsTypeEnum.deliveryDueDate: @@ -80,6 +94,22 @@ extension HealthCalculatorExtenshion on HealthCalculatorsTypeEnum { case HealthCalculatorsTypeEnum.triglycerides: return isArabic ? "الدهون الثلاثية في الدم" : "Triglycerides Fat Blood"; } + } + + String get title { + AppState appState = getIt.get(); + bool isArabic = appState.getLanguageID() == 1 ? true : false; + switch (this) { + case HealthCalculatorsTypeEnum.crabsProteinFat: + return isArabic ? "نتيجة المغذيات" : "Nutrient Results"; + case HealthCalculatorsTypeEnum.calories: + return isArabic ? "سعراتك الحرارية" : "Calories"; + + case HealthCalculatorsTypeEnum.calories: + return isArabic ? "مؤشر كتلة الجسم" : "BMI"; + default: + return "Your ${this.name.toCamelCase}"; + } } static LoginTypeEnum? fromValue(int value) { diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index c7c924ef..581f1ea0 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1158,11 +1158,18 @@ abstract class LocaleKeys { static const recharge = 'recharge'; static const activityLevel = 'activityLevel'; static const selectActivityLevel = 'selectActivityLevel'; + static const almostInactive = 'almostInactive'; + static const lightlyActive = 'lightlyActive'; + static const lightlyActive13Days = 'lightlyActive13Days'; + static const superActive = 'superActive'; static const caloriesPerDay = 'caloriesPerDay'; static const dietType = 'dietType'; static const selectDietType = 'selectDietType'; static const bodyFrameSize = 'bodyFrameSize'; static const selectBodyFrameSize = 'selectBodyFrameSize'; + static const bodyFrameSmall = 'bodyFrameSmall'; + static const bodyFrameMedium = 'bodyFrameMedium'; + static const bodyFrameLarge = 'bodyFrameLarge'; static const averageCycleLength = 'averageCycleLength'; static const averageLutealPhase = 'averageLutealPhase'; static const convert = 'convert'; @@ -1330,6 +1337,7 @@ abstract class LocaleKeys { static const askYourDoctor = 'askYourDoctor'; static const internetPairing = 'internetPairing'; static const requested = 'requested'; + static const readyToDownload = 'readyToDownload'; static const youDontHaveAnyMedicalReportsYet = 'youDontHaveAnyMedicalReportsYet'; static const requestMedicalReport = 'requestMedicalReport'; static const youDoNotHaveAnyAppointmentsToRequestMedicalReport = 'youDoNotHaveAnyAppointmentsToRequestMedicalReport'; @@ -1759,5 +1767,14 @@ abstract class LocaleKeys { static const locationServicesDisabled = 'locationServicesDisabled'; static const selectDateTimeKey = 'selectDateTimeKey'; static const medicalKey = 'medicalKey'; + static const convertBloodcholesterolInfo = 'convertBloodcholesterolInfo'; + static const carbsAlert = 'carbsAlert'; + static const bodyFatAlert = 'bodyFatAlert'; + static const ibwAlert = 'ibwAlert'; + static const glucoseAlert = 'glucoseAlert'; + static const selectBloodGroup = 'selectBloodGroup'; + static const routePrescription = 'routePrescription'; + static const noNearestER = 'noNearestER'; + static const loginToUseServiceLiveCare = 'loginToUseServiceLiveCare'; } diff --git a/lib/presentation/blood_donation/blood_donation_page.dart b/lib/presentation/blood_donation/blood_donation_page.dart index c6767c2d..4a2a7cc4 100644 --- a/lib/presentation/blood_donation/blood_donation_page.dart +++ b/lib/presentation/blood_donation/blood_donation_page.dart @@ -219,7 +219,7 @@ class _BloodDonationPageState extends State { ], ).onPress(() { showCommonBottomSheetWithoutHeight(context, - title: LocaleKeys.select.tr(context: context), + title: LocaleKeys.selectBloodGroup.tr(context: context), isDismissible: true, child: SelectBloodGroupWidget(isArabic: appState.isArabic(), bloodDonationViewModel: bloodDonationVM), callBackFunc: () {}); diff --git a/lib/presentation/blood_donation/widgets/select_gender_widget.dart b/lib/presentation/blood_donation/widgets/select_gender_widget.dart index 0d360a80..fc8863d8 100644 --- a/lib/presentation/blood_donation/widgets/select_gender_widget.dart +++ b/lib/presentation/blood_donation/widgets/select_gender_widget.dart @@ -37,7 +37,7 @@ class SelectGenderWidget extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: 8.h, - children: [GenderTypeEnum.values[index].name.toCamelCase.toText16(color: AppColors.textColor, isBold: true)], + children: [GenderTypeEnum.values[index].displayName.toCamelCase.toText16(color: AppColors.textColor, isBold: true)], ), ), Transform.flip( diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index d0ef2a4f..15b019e9 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -907,7 +907,7 @@ class _BookAppointmentPageState extends State { ), SizedBox(height: 36.h), CustomButton( - text: LocaleKeys.loginToUseService.tr(), + text: LocaleKeys.loginToUseServiceLiveCare.tr(), onPressed: () async { await authVM.onLoginPressed(); }, diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index 131b4d3d..fc102b4d 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -80,7 +80,7 @@ class _SelectClinicPageState extends State { appState = getIt.get(); if (widget.calculatorType != null) { return CollapsingListView( - title: "Your ${widget.calculatorType!.name.toCamelCase}", + title: widget.calculatorType!.title, bottomChild: Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.only(topLeft: Radius.circular(24.r), topRight: Radius.circular(24.r))), padding: EdgeInsets.symmetric(vertical: 20.h, horizontal: 20.h), diff --git a/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart index 589764c1..07e26b6f 100644 --- a/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart +++ b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart @@ -250,7 +250,7 @@ class _ComprehensiveCheckupPageState extends State { physics: NeverScrollableScrollPhysics(), itemBuilder: (context, index) { final service = services[index]; - final serviceName = service.text ?? service.textN ?? ''; + final serviceName = getIt.get().isArabic()? service.textN??service.text??"" : service.text??service.textN ?? ''; final price = service.priceTotal ?? 0.0; return RadioListTileWidget( value: service.iD ?? 0, diff --git a/lib/presentation/emergency_services/emergency_services_page.dart b/lib/presentation/emergency_services/emergency_services_page.dart index 3d5470a8..2d4ef828 100644 --- a/lib/presentation/emergency_services/emergency_services_page.dart +++ b/lib/presentation/emergency_services/emergency_services_page.dart @@ -90,7 +90,7 @@ class _EmergencyServicesPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.callAmbulance.tr().toText16(isBold: true, color: AppColors.blackColor), - LocaleKeys.requestAmbulanceInEmergency.tr().toText12(color: AppColors.greyTextColor, isBold: true), + LocaleKeys.requestAmbulanceInEmergency.tr().toText12(color: AppColors.greyTextColor, isBold: true), ], ), ), diff --git a/lib/presentation/emergency_services/nearest_er_page.dart b/lib/presentation/emergency_services/nearest_er_page.dart index ea9a1517..97e601d5 100644 --- a/lib/presentation/emergency_services/nearest_er_page.dart +++ b/lib/presentation/emergency_services/nearest_er_page.dart @@ -92,7 +92,7 @@ class _NearestErPageState extends State { }, ); } else { - return Center(child: Utils.getNoDataWidget(context, noDataText: "No nearest Er Around you")); + return Center(child: Utils.getNoDataWidget(context, noDataText: LocaleKeys.noNearestER.tr())); } }), ), diff --git a/lib/presentation/health_calculators_and_converts/health_calculators_page.dart b/lib/presentation/health_calculators_and_converts/health_calculators_page.dart index a7fe0ef5..8b351ecf 100644 --- a/lib/presentation/health_calculators_and_converts/health_calculators_page.dart +++ b/lib/presentation/health_calculators_and_converts/health_calculators_page.dart @@ -146,7 +146,10 @@ class _HealthCalculatorsPageState extends State { SizedBox( width: 12.w, ), - Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor), + Transform.flip( + flipX: getIt.get().isArabic(), + child: Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, + fit: BoxFit.contain, iconColor: AppColors.textColor),), ], ).paddingAll(16.w)) .onPress(() { @@ -172,8 +175,10 @@ class _HealthCalculatorsPageState extends State { ), ), SizedBox(width: 12.w), - Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor), - ], + Transform.flip( + flipX: getIt.get().isArabic(), + child: Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, + fit: BoxFit.contain, iconColor: AppColors.textColor),), ], ).paddingAll(16.w)) .onPress(() { Navigator.of(context).push( @@ -201,8 +206,10 @@ class _HealthCalculatorsPageState extends State { ), ), SizedBox(width: 12.w), - Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor), - ], + Transform.flip( + flipX: getIt.get().isArabic(), + child: Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, + fit: BoxFit.contain, iconColor: AppColors.textColor),), ], ).paddingAll(16.w)) .onPress(() { Navigator.of(context).push( diff --git a/lib/presentation/health_calculators_and_converts/widgets/bf.dart b/lib/presentation/health_calculators_and_converts/widgets/bf.dart index d71566a2..a2d804b0 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/bf.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/bf.dart @@ -26,7 +26,19 @@ class _BodyFatWidgetState extends State { final TextEditingController _neckController = TextEditingController(); final TextEditingController _waistController = TextEditingController(); final TextEditingController _hipController = TextEditingController(); - final List _heightUnits = ['cm', 'm', 'ft', 'in']; + + final Map _heightUnits = { + 'cm': LocaleKeys.cm, + 'm': LocaleKeys.m, + 'ft': LocaleKeys.ft, + 'in': LocaleKeys.inch, + }; + + final Map _genders = { + 'Male': LocaleKeys.malE, + 'Female': LocaleKeys.femaleGender, + }; + String selectedHeightUnit = 'cm'; String selectedNeckUnit = 'in'; String selectedWaistUnit = 'in'; @@ -82,6 +94,7 @@ class _BodyFatWidgetState extends State { decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true), child: Column( children: [ + // === GENDER ROW === Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -95,7 +108,7 @@ class _BodyFatWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ LocaleKeys.selectGender.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedGender.toText12(isBold: true, color: AppColors.textColor), + (_genders[selectedGender] ?? selectedGender).tr().toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -105,7 +118,6 @@ class _BodyFatWidgetState extends State { SizedBox(width: 4.w), ], ).paddingSymmetrical(0.w, 16.w).onPress(() { - List _genders = ["Male", "Female"]; dialogService.showFamilyBottomSheetWithoutHWithChild( label: LocaleKeys.selectGender.tr(context: context), message: "", @@ -116,7 +128,8 @@ class _BodyFatWidgetState extends State { shrinkWrap: true, itemCount: _genders.length, itemBuilder: (context, index) { - final unit = _genders[index]; + final key = _genders.keys.elementAt(index); + final localeKey = _genders[key]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -127,7 +140,7 @@ class _BodyFatWidgetState extends State { spacing: 8.h, children: [ Radio( - value: unit, + value: key, groupValue: selectedGender, activeColor: AppColors.errorColor, onChanged: (String? value) { @@ -140,13 +153,13 @@ class _BodyFatWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { - selectedGender = unit; - _onInputChanged(); + selectedGender = key; }); + _onInputChanged(); Navigator.pop(context); }), ), @@ -162,6 +175,7 @@ class _BodyFatWidgetState extends State { ); }), Divider(height: 1, color: Color(0xFFEEEEEE)), + // === HEIGHT ROW === Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -208,72 +222,24 @@ class _BodyFatWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedHeightUnit.toText12(isBold: true, color: AppColors.textColor), + LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + (_heightUnits[selectedHeightUnit] ?? selectedHeightUnit).tr().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), Utils.buildSvgWithAssets(icon: AppAssets.arrow_down) ], ).onPress(() { - dialogService.showFamilyBottomSheetWithoutHWithChild( - label: LocaleKeys.unit.tr(context: context), - message: "", - child: Container( - padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h), - decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)), - child: ListView.separated( - shrinkWrap: true, - itemCount: _heightUnits.length, - itemBuilder: (context, index) { - final unit = _heightUnits[index]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox( - height: 70, - child: Row( - spacing: 8.h, - children: [ - Radio( - value: unit, - groupValue: selectedHeightUnit, - activeColor: AppColors.errorColor, - onChanged: (String? value) { - if (value == null) return; - setState(() { - selectedHeightUnit = value; - }); - Navigator.pop(context); - }, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, - ], - ).onPress(() { - setState(() { - selectedHeightUnit = unit; - _onInputChanged(); - }); - Navigator.pop(context); - }), - ), - ], - ); - }, - separatorBuilder: (BuildContext context, int index) { - return Divider(height: 1, color: Color(0xFFEEEEEE)); - }, - ), - ), - onOkPressed: () {}, - ); + _showUnitBottomSheet(context, dialogService, selectedHeightUnit, (value) { + setState(() { selectedHeightUnit = value; }); + _onInputChanged(); + }); }), ), ], ).paddingSymmetrical(0.w, 16.w), Divider(height: 1, color: Color(0xFFEEEEEE)), + // === NECK ROW === Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -302,12 +268,7 @@ class _BodyFatWidgetState extends State { hintText: '7', hintStyle: TextStyle(color: Colors.grey), ), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.black87, - height: 1.0, - ), + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0), ), ) ], @@ -326,71 +287,23 @@ class _BodyFatWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedNeckUnit.toText12(isBold: true, color: AppColors.textColor), + (_heightUnits[selectedNeckUnit] ?? selectedNeckUnit).tr().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), Utils.buildSvgWithAssets(icon: AppAssets.arrow_down) ], ).onPress(() { - dialogService.showFamilyBottomSheetWithoutHWithChild( - label: LocaleKeys.unit.tr(context: context), - message: "", - child: Container( - padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h), - decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)), - child: ListView.separated( - shrinkWrap: true, - itemCount: _heightUnits.length, - itemBuilder: (context, index) { - final unit = _heightUnits[index]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox( - height: 70, - child: Row( - spacing: 8.h, - children: [ - Radio( - value: unit, - groupValue: selectedNeckUnit, - activeColor: AppColors.errorColor, - onChanged: (String? value) { - if (value == null) return; - setState(() { - selectedNeckUnit = value; - }); - Navigator.pop(context); - }, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, - ], - ).onPress(() { - setState(() { - selectedNeckUnit = unit; - _onInputChanged(); - }); - Navigator.pop(context); - }), - ), - ], - ); - }, - separatorBuilder: (BuildContext context, int index) { - return Divider(height: 1, color: Color(0xFFEEEEEE)); - }, - ), - ), - onOkPressed: () {}, - ); + _showUnitBottomSheet(context, dialogService, selectedNeckUnit, (value) { + setState(() { selectedNeckUnit = value; }); + _onInputChanged(); + }); }), ), ], ).paddingSymmetrical(0.w, 16.w), Divider(height: 1, color: Color(0xFFEEEEEE)), + // === WAIST ROW === Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -419,12 +332,7 @@ class _BodyFatWidgetState extends State { hintText: '30', hintStyle: TextStyle(color: Colors.grey), ), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.black87, - height: 1.0, - ), + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0), ), ) ], @@ -442,72 +350,24 @@ class _BodyFatWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedWaistUnit.toText12(isBold: true, color: AppColors.textColor), + LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + (_heightUnits[selectedWaistUnit] ?? selectedWaistUnit).tr().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), Utils.buildSvgWithAssets(icon: AppAssets.arrow_down) ], ).onPress(() { - dialogService.showFamilyBottomSheetWithoutHWithChild( - label: LocaleKeys.unit.tr(context: context), - message: "", - child: Container( - padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h), - decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)), - child: ListView.separated( - shrinkWrap: true, - itemCount: _heightUnits.length, - itemBuilder: (context, index) { - final unit = _heightUnits[index]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox( - height: 70, - child: Row( - spacing: 8.h, - children: [ - Radio( - value: unit, - groupValue: selectedWaistUnit, - activeColor: AppColors.errorColor, - onChanged: (String? value) { - if (value == null) return; - setState(() { - selectedWaistUnit = value; - }); - Navigator.pop(context); - }, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, - ], - ).onPress(() { - setState(() { - selectedWaistUnit = unit; - _onInputChanged(); - }); - Navigator.pop(context); - }), - ), - ], - ); - }, - separatorBuilder: (BuildContext context, int index) { - return Divider(height: 1, color: Color(0xFFEEEEEE)); - }, - ), - ), - onOkPressed: () {}, - ); + _showUnitBottomSheet(context, dialogService, selectedWaistUnit, (value) { + setState(() { selectedWaistUnit = value; }); + _onInputChanged(); + }); }), ), ], ).paddingSymmetrical(0.w, 16.w), Divider(height: 1, color: Color(0xFFEEEEEE)), + // === HIP ROW === Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -536,12 +396,7 @@ class _BodyFatWidgetState extends State { hintText: '0', hintStyle: TextStyle(color: Colors.grey), ), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.black87, - height: 1.0, - ), + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0), ), ) ], @@ -560,66 +415,17 @@ class _BodyFatWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedHipUnit.toText12(isBold: true, color: AppColors.textColor), + (_heightUnits[selectedHipUnit] ?? selectedHipUnit).tr().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), Utils.buildSvgWithAssets(icon: AppAssets.arrow_down) ], ).onPress(() { - dialogService.showFamilyBottomSheetWithoutHWithChild( - label: LocaleKeys.unit.tr(context: context), - message: "", - child: Container( - padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h), - decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)), - child: ListView.separated( - shrinkWrap: true, - itemCount: _heightUnits.length, - itemBuilder: (context, index) { - final unit = _heightUnits[index]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox( - height: 70, - child: Row( - spacing: 8.h, - children: [ - Radio( - value: unit, - groupValue: selectedHipUnit, - activeColor: AppColors.errorColor, - onChanged: (String? value) { - if (value == null) return; - setState(() { - selectedHipUnit = value; - }); - Navigator.pop(context); - }, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, - ], - ).onPress(() { - setState(() { - selectedHipUnit = unit; - _onInputChanged(); - }); - Navigator.pop(context); - }), - ), - ], - ); - }, - separatorBuilder: (BuildContext context, int index) { - return Divider(height: 1, color: Color(0xFFEEEEEE)); - }, - ), - ), - onOkPressed: () {}, - ); + _showUnitBottomSheet(context, dialogService, selectedHipUnit, (value) { + setState(() { selectedHipUnit = value; }); + _onInputChanged(); + }); }), ), ], @@ -631,7 +437,7 @@ class _BodyFatWidgetState extends State { Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w), SizedBox(width: 12.w), Expanded( - child: "Estimate the total body fat based on the size.".toText12(isBold: true, color: AppColors.inputLabelTextColor), + child: LocaleKeys.bodyFatAlert.tr().toText12(isBold: true, color: AppColors.inputLabelTextColor), ), ], ).paddingSymmetrical(0.w, 16.w), @@ -640,4 +446,56 @@ class _BodyFatWidgetState extends State { ); }); } + + void _showUnitBottomSheet(BuildContext context, DialogService dialogService, String currentValue, Function(String) onSelected) { + dialogService.showFamilyBottomSheetWithoutHWithChild( + label: LocaleKeys.unit.tr(context: context), + message: "", + child: Container( + padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h), + decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)), + child: ListView.separated( + shrinkWrap: true, + itemCount: _heightUnits.length, + itemBuilder: (context, index) { + final key = _heightUnits.keys.elementAt(index); + final localeKey = _heightUnits[key]!; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + height: 70, + child: Row( + spacing: 8.h, + children: [ + Radio( + value: key, + groupValue: currentValue, + activeColor: AppColors.errorColor, + onChanged: (String? value) { + if (value == null) return; + onSelected(value); + Navigator.pop(context); + }, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + ], + ).onPress(() { + onSelected(key); + Navigator.pop(context); + }), + ), + ], + ); + }, + separatorBuilder: (BuildContext context, int index) { + return Divider(height: 1, color: Color(0xFFEEEEEE)); + }, + ), + ), + onOkPressed: () {}, + ); + } } diff --git a/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart b/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart index f2110646..670236de 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart @@ -1,4 +1,6 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; @@ -129,7 +131,7 @@ class _BloodSugarWidgetState extends State { ), SizedBox(width: 12.w), Expanded( - child: "Convert blood glucose values between mg/dL and mmol/L. (1 mmol/L ≈ 18.0182 mg/dL)".toText12( + child: LocaleKeys.glucoseAlert.tr().toText12( isBold: true, color: AppColors.inputLabelTextColor, ), diff --git a/lib/presentation/health_calculators_and_converts/widgets/bmr.dart b/lib/presentation/health_calculators_and_converts/widgets/bmr.dart index 003faf06..2b013549 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/bmr.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/bmr.dart @@ -26,8 +26,31 @@ class _BMRWidgetState extends State { final TextEditingController _heightController = TextEditingController(); final TextEditingController _weightController = TextEditingController(); final TextEditingController _ageController = TextEditingController(); - final List _heightUnits = ['cm', 'm', 'ft', 'in']; - final List _weightUnits = ['kg', 'lb']; + + final Map _heightUnits = { + 'cm': LocaleKeys.cm, + 'm': LocaleKeys.m, + 'ft': LocaleKeys.ft, + 'in': LocaleKeys.inch, + }; + + final Map _weightUnits = { + 'kg': LocaleKeys.kg, + 'lb': LocaleKeys.lb, + }; + + final Map _genders = { + 'Male': LocaleKeys.malE, + 'Female': LocaleKeys.femaleGender, + }; + + final Map _activityLevels = { + 'Almost Inactive (no exercise)': LocaleKeys.almostInactive, + 'Lightly active': LocaleKeys.lightlyActive, + 'Lightly active (1-3) days per week': LocaleKeys.lightlyActive13Days, + 'Super active (very hard exercise)': LocaleKeys.superActive, + }; + String selectedHeightUnit = 'cm'; String selectedWeightUnit = 'kg'; String selectedActivityLevel = 'Lightly active'; @@ -90,7 +113,7 @@ class _BMRWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ LocaleKeys.selectGender.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedGender.toCamelCase.toText12(isBold: true, color: AppColors.textColor), + (_genders[selectedGender] ?? selectedGender).tr().toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -100,8 +123,6 @@ class _BMRWidgetState extends State { SizedBox(width: 4.w), ], ).paddingSymmetrical(0.w, 16.w).onPress(() { - List _genders = ["Male", "Female"]; - dialogService.showFamilyBottomSheetWithoutHWithChild( label: LocaleKeys.selectGender.tr(context: context), message: "", @@ -112,7 +133,8 @@ class _BMRWidgetState extends State { shrinkWrap: true, itemCount: _genders.length, itemBuilder: (context, index) { - final unit = _genders[index]; + final key = _genders.keys.elementAt(index); + final localeKey = _genders[key]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -123,12 +145,11 @@ class _BMRWidgetState extends State { spacing: 8.h, children: [ Radio( - value: unit, + value: key, groupValue: selectedGender, activeColor: AppColors.errorColor, onChanged: (String? value) { if (value == null) return; - print(value); setState(() { selectedGender = value; }); @@ -137,11 +158,11 @@ class _BMRWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { - selectedGender = unit; + selectedGender = key; }); _onInputChanged(); Navigator.pop(context); @@ -243,7 +264,7 @@ class _BMRWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedHeightUnit.toText12(isBold: true, color: AppColors.textColor), + (_heightUnits[selectedHeightUnit] ?? selectedHeightUnit).tr().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -260,7 +281,8 @@ class _BMRWidgetState extends State { shrinkWrap: true, itemCount: _heightUnits.length, itemBuilder: (context, index) { - final unit = _heightUnits[index]; + final key = _heightUnits.keys.elementAt(index); + final localeKey = _heightUnits[key]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -271,7 +293,7 @@ class _BMRWidgetState extends State { spacing: 8.h, children: [ Radio( - value: unit, + value: key, groupValue: selectedHeightUnit, activeColor: AppColors.errorColor, onChanged: (String? value) { @@ -284,13 +306,13 @@ class _BMRWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { - selectedHeightUnit = unit; - _onInputChanged(); + selectedHeightUnit = key; }); + _onInputChanged(); Navigator.pop(context); }), ), @@ -361,7 +383,7 @@ class _BMRWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedWeightUnit.toText12(isBold: true, color: AppColors.textColor), + (_weightUnits[selectedWeightUnit] ?? selectedWeightUnit).tr().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -378,7 +400,8 @@ class _BMRWidgetState extends State { shrinkWrap: true, itemCount: _weightUnits.length, itemBuilder: (context, index) { - final unit = _weightUnits[index]; + final key = _weightUnits.keys.elementAt(index); + final localeKey = _weightUnits[key]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -389,7 +412,7 @@ class _BMRWidgetState extends State { spacing: 8.h, children: [ Radio( - value: unit, + value: key, groupValue: selectedWeightUnit, activeColor: AppColors.errorColor, onChanged: (String? value) { @@ -402,13 +425,13 @@ class _BMRWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { - selectedWeightUnit = unit; - _onInputChanged(); + selectedWeightUnit = key; }); + _onInputChanged(); Navigator.pop(context); }), ), @@ -440,7 +463,7 @@ class _BMRWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ LocaleKeys.activityLevel.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedActivityLevel.toText12(isBold: true, color: AppColors.textColor), + (_activityLevels[selectedActivityLevel] ?? selectedActivityLevel).tr().toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -450,7 +473,6 @@ class _BMRWidgetState extends State { SizedBox(width: 4.w), ], ).paddingSymmetrical(0.w, 16.w).onPress(() { - List _activity = ["Almost Inactive (no exercise)", "Lightly active", "Lightly active (1-3) days per week", "Super active (very hard exercise)"]; dialogService.showFamilyBottomSheetWithoutHWithChild( label: LocaleKeys.selectActivityLevel.tr(context: context), message: "", @@ -459,9 +481,10 @@ class _BMRWidgetState extends State { decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)), child: ListView.separated( shrinkWrap: true, - itemCount: _activity.length, + itemCount: _activityLevels.length, itemBuilder: (context, index) { - final unit = _activity[index]; + final key = _activityLevels.keys.elementAt(index); + final localeKey = _activityLevels[key]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -472,7 +495,7 @@ class _BMRWidgetState extends State { spacing: 8.h, children: [ Radio( - value: unit, + value: key, groupValue: selectedActivityLevel, activeColor: AppColors.errorColor, onChanged: (String? value) { @@ -485,13 +508,13 @@ class _BMRWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { - selectedActivityLevel = unit; - _onInputChanged(); + selectedActivityLevel = key; }); + _onInputChanged(); Navigator.pop(context); }), ), diff --git a/lib/presentation/health_calculators_and_converts/widgets/calories.dart b/lib/presentation/health_calculators_and_converts/widgets/calories.dart index d354ce8e..ad31cd6c 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/calories.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/calories.dart @@ -26,8 +26,31 @@ class _CaloriesWidgetState extends State { final TextEditingController _heightController = TextEditingController(); final TextEditingController _weightController = TextEditingController(); final TextEditingController _ageController = TextEditingController(); - final List _heightUnits = ['cm', 'm', 'ft', 'in']; - final List _weightUnits = ['kg', 'lb']; + + final Map _heightUnits = { + 'cm': LocaleKeys.cm, + 'm': LocaleKeys.m, + 'ft': LocaleKeys.ft, + 'in': LocaleKeys.inch, + }; + + final Map _weightUnits = { + 'kg': LocaleKeys.kg, + 'lb': LocaleKeys.lb, + }; + + final Map _genders = { + 'Male': LocaleKeys.malE, + 'Female': LocaleKeys.femaleGender, + }; + + final Map _activityLevels = { + 'Almost Inactive (no exercise)': LocaleKeys.almostInactive, + 'Lightly active': LocaleKeys.lightlyActive, + 'Lightly active (1-3) days per week': LocaleKeys.lightlyActive13Days, + 'Super active (very hard exercise)': LocaleKeys.superActive, + }; + String selectedHeightUnit = 'cm'; String selectedWeightUnit = 'kg'; String selectedActivityLevel = 'Lightly active'; @@ -69,7 +92,6 @@ class _CaloriesWidgetState extends State { @override Widget build(BuildContext context) { DialogService dialogService = getIt(); - AppState appState = getIt(); return Consumer(builder: (context, provider, _) { return Container( @@ -78,6 +100,7 @@ class _CaloriesWidgetState extends State { decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true), child: Column( children: [ + // === GENDER ROW === Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -90,8 +113,8 @@ class _CaloriesWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Select Gender".toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedGender.toCamelCase.toText12(isBold: true, color: AppColors.textColor), + LocaleKeys.selectGender.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + (_genders[selectedGender] ?? selectedGender).tr().toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -101,8 +124,6 @@ class _CaloriesWidgetState extends State { SizedBox(width: 4.w), ], ).paddingSymmetrical(0.w, 16.w).onPress(() { - List _genders = ["Male", "Female"]; - dialogService.showFamilyBottomSheetWithoutHWithChild( label: LocaleKeys.selectGender.tr(context: context), message: "", @@ -113,7 +134,8 @@ class _CaloriesWidgetState extends State { shrinkWrap: true, itemCount: _genders.length, itemBuilder: (context, index) { - final unit = _genders[index]; + final key = _genders.keys.elementAt(index); + final localeKey = _genders[key]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -124,7 +146,7 @@ class _CaloriesWidgetState extends State { spacing: 8.h, children: [ Radio( - value: unit, + value: key, groupValue: selectedGender, activeColor: AppColors.errorColor, onChanged: (String? value) { @@ -137,13 +159,13 @@ class _CaloriesWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { - selectedGender = unit; - _onInputChanged(); + selectedGender = key; }); + _onInputChanged(); Navigator.pop(context); }), ), @@ -159,6 +181,7 @@ class _CaloriesWidgetState extends State { ); }), Divider(height: 1, color: Color(0xFFEEEEEE)), + // === AGE ROW (unchanged) === Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -196,6 +219,7 @@ class _CaloriesWidgetState extends State { ], ).paddingSymmetrical(0.w, 16.w), Divider(height: 1, color: Color(0xFFEEEEEE)), + // === HEIGHT ROW === Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -242,72 +266,24 @@ class _CaloriesWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedHeightUnit.toText12(isBold: true, color: AppColors.textColor), + LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + (_heightUnits[selectedHeightUnit] ?? selectedHeightUnit).tr().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), Utils.buildSvgWithAssets(icon: AppAssets.arrow_down) ], ).onPress(() { - dialogService.showFamilyBottomSheetWithoutHWithChild( - label: LocaleKeys.unit.tr(context: context), - message: "", - child: Container( - padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h), - decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)), - child: ListView.separated( - shrinkWrap: true, - itemCount: _heightUnits.length, - itemBuilder: (context, index) { - final unit = _heightUnits[index]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox( - height: 70, - child: Row( - spacing: 8.h, - children: [ - Radio( - value: unit, - groupValue: selectedHeightUnit, - activeColor: AppColors.errorColor, - onChanged: (String? value) { - if (value == null) return; - setState(() { - selectedHeightUnit = value; - }); - Navigator.pop(context); - }, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, - ], - ).onPress(() { - setState(() { - selectedHeightUnit = unit; - _onInputChanged(); - }); - Navigator.pop(context); - }), - ), - ], - ); - }, - separatorBuilder: (BuildContext context, int index) { - return Divider(height: 1, color: Color(0xFFEEEEEE)); - }, - ), - ), - onOkPressed: () {}, - ); + _showUnitBottomSheet(context, dialogService, _heightUnits, selectedHeightUnit, (value) { + setState(() { selectedHeightUnit = value; }); + _onInputChanged(); + }); }), ), ], ).paddingSymmetrical(0.w, 16.w), Divider(height: 1, color: Color(0xFFEEEEEE)), + // === WEIGHT ROW === Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -359,73 +335,24 @@ class _CaloriesWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedWeightUnit.toText12(isBold: true, color: AppColors.textColor), + LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + (_weightUnits[selectedWeightUnit] ?? selectedWeightUnit).tr().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), Utils.buildSvgWithAssets(icon: AppAssets.arrow_down) ], ).onPress(() { - dialogService.showFamilyBottomSheetWithoutHWithChild( - label: LocaleKeys.unit.tr(context: context), - message: "", - child: Container( - padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h), - decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)), - child: ListView.separated( - shrinkWrap: true, - itemCount: _weightUnits.length, - itemBuilder: (context, index) { - final unit = _weightUnits[index]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox( - height: 70, - child: Row( - spacing: 8.h, - children: [ - Radio( - value: unit, - groupValue: selectedWeightUnit, - activeColor: AppColors.errorColor, - onChanged: (String? value) { - if (value == null) return; - setState(() { - selectedWeightUnit = value; - }); - _onInputChanged(); - Navigator.pop(context); - }, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, - ], - ).onPress(() { - setState(() { - selectedWeightUnit = unit; - _onInputChanged(); - }); - Navigator.pop(context); - }), - ), - ], - ); - }, - separatorBuilder: (BuildContext context, int index) { - return Divider(height: 1, color: Color(0xFFEEEEEE)); - }, - ), - ), - onOkPressed: () {}, - ); + _showUnitBottomSheet(context, dialogService, _weightUnits, selectedWeightUnit, (value) { + setState(() { selectedWeightUnit = value; }); + _onInputChanged(); + }); }), ), ], ).paddingSymmetrical(0.w, 16.w), Divider(height: 1, color: Color(0xFFEEEEEE)), + // === ACTIVITY LEVEL ROW === Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -439,7 +366,7 @@ class _CaloriesWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ LocaleKeys.activityLevel.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedActivityLevel.toText12(isBold: true, color: AppColors.textColor), + (_activityLevels[selectedActivityLevel] ?? selectedActivityLevel).tr().toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -449,7 +376,6 @@ class _CaloriesWidgetState extends State { SizedBox(width: 4.w), ], ).paddingSymmetrical(0.w, 16.w).onPress(() { - List _activity = ["Almost Inactive (no exercise)", "Lightly active", "Lightly active (1-3) days per week", "Super active (very hard exercise)"]; dialogService.showFamilyBottomSheetWithoutHWithChild( label: LocaleKeys.selectActivityLevel.tr(context: context), message: "", @@ -458,9 +384,10 @@ class _CaloriesWidgetState extends State { decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)), child: ListView.separated( shrinkWrap: true, - itemCount: _activity.length, + itemCount: _activityLevels.length, itemBuilder: (context, index) { - final unit = _activity[index]; + final key = _activityLevels.keys.elementAt(index); + final localeKey = _activityLevels[key]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -471,7 +398,7 @@ class _CaloriesWidgetState extends State { spacing: 8.h, children: [ Radio( - value: unit, + value: key, groupValue: selectedActivityLevel, activeColor: AppColors.errorColor, onChanged: (String? value) { @@ -484,13 +411,13 @@ class _CaloriesWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { - selectedActivityLevel = unit; - _onInputChanged(); + selectedActivityLevel = key; }); + _onInputChanged(); Navigator.pop(context); }), ), @@ -522,4 +449,56 @@ class _CaloriesWidgetState extends State { ); }); } + + void _showUnitBottomSheet(BuildContext context, DialogService dialogService, Map units, String currentValue, Function(String) onSelected) { + dialogService.showFamilyBottomSheetWithoutHWithChild( + label: LocaleKeys.unit.tr(context: context), + message: "", + child: Container( + padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h), + decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)), + child: ListView.separated( + shrinkWrap: true, + itemCount: units.length, + itemBuilder: (context, index) { + final key = units.keys.elementAt(index); + final localeKey = units[key]!; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + height: 70, + child: Row( + spacing: 8.h, + children: [ + Radio( + value: key, + groupValue: currentValue, + activeColor: AppColors.errorColor, + onChanged: (String? value) { + if (value == null) return; + onSelected(value); + Navigator.pop(context); + }, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + ], + ).onPress(() { + onSelected(key); + Navigator.pop(context); + }), + ), + ], + ); + }, + separatorBuilder: (BuildContext context, int index) { + return Divider(height: 1, color: Color(0xFFEEEEEE)); + }, + ), + ), + onOkPressed: () {}, + ); + } } diff --git a/lib/presentation/health_calculators_and_converts/widgets/crabs.dart b/lib/presentation/health_calculators_and_converts/widgets/crabs.dart index e3e7c1e3..8a2f6343 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/crabs.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/crabs.dart @@ -187,7 +187,7 @@ class _CrabsWidgetState extends State { Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w), SizedBox(width: 12.w), Expanded( - child: "Calculate carbohydrate based protein and fat ration in calories and grams according to a pre set ratio." + child: LocaleKeys.carbsAlert.tr() .toText12(isBold: true, color: AppColors.inputLabelTextColor), ), ], diff --git a/lib/presentation/health_calculators_and_converts/widgets/ibw.dart b/lib/presentation/health_calculators_and_converts/widgets/ibw.dart index 070e373e..f9d7e964 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/ibw.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/ibw.dart @@ -24,12 +24,24 @@ class IdealBodyWeightWidget extends StatefulWidget { class _IdealBodyWeightWidgetState extends State { final TextEditingController _heightController = TextEditingController(); final TextEditingController _weightController = TextEditingController(); - final List _heightUnits = ['cm', 'm', 'ft', 'in']; - final List _weightUnits = ['kg', 'lb']; + final Map _heightUnits = { + 'cm': LocaleKeys.cm, + 'm': LocaleKeys.m, + 'ft': LocaleKeys.ft, + 'in': LocaleKeys.inch, + }; + final Map _weightUnits = { + 'kg': LocaleKeys.kg, + 'lb': LocaleKeys.lb, + }; + final Map _bodyFrameSizes = { + 'Small (fingers overlaps)': LocaleKeys.bodyFrameSmall, + 'Medium (fingers touch)': LocaleKeys.bodyFrameMedium, + "Large (fingers don't touch)": LocaleKeys.bodyFrameLarge, + }; String selectedHeightUnit = 'cm'; String selectedWeightUnit = 'kg'; String selectedBodyFrameSize = 'Medium (fingers touch)'; - Function(String)? onUnitSelected; @override void initState() { @@ -117,7 +129,7 @@ class _IdealBodyWeightWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedHeightUnit.toText12(isBold: true, color: AppColors.textColor), + (_heightUnits[selectedHeightUnit] ?? selectedHeightUnit).tr().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -134,7 +146,8 @@ class _IdealBodyWeightWidgetState extends State { shrinkWrap: true, itemCount: _heightUnits.length, itemBuilder: (context, index) { - final unit = _heightUnits[index]; + final key = _heightUnits.keys.elementAt(index); + final localeKey = _heightUnits[key]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -145,7 +158,7 @@ class _IdealBodyWeightWidgetState extends State { spacing: 8.h, children: [ Radio( - value: unit, + value: key, groupValue: selectedHeightUnit, activeColor: AppColors.errorColor, onChanged: (String? value) { @@ -153,17 +166,18 @@ class _IdealBodyWeightWidgetState extends State { setState(() { selectedHeightUnit = value; }); + _onInputChanged(); Navigator.pop(context); }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { - selectedHeightUnit = unit; - _onInputChanged(); + selectedHeightUnit = key; }); + _onInputChanged(); Navigator.pop(context); }), ), @@ -234,7 +248,7 @@ class _IdealBodyWeightWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedWeightUnit.toText12(isBold: true, color: AppColors.textColor), + (_weightUnits[selectedWeightUnit] ?? selectedWeightUnit).tr().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -251,7 +265,8 @@ class _IdealBodyWeightWidgetState extends State { shrinkWrap: true, itemCount: _weightUnits.length, itemBuilder: (context, index) { - final unit = _weightUnits[index]; + final key = _weightUnits.keys.elementAt(index); + final localeKey = _weightUnits[key]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -262,7 +277,7 @@ class _IdealBodyWeightWidgetState extends State { spacing: 8.h, children: [ Radio( - value: unit, + value: key, groupValue: selectedWeightUnit, activeColor: AppColors.errorColor, onChanged: (String? value) { @@ -270,16 +285,18 @@ class _IdealBodyWeightWidgetState extends State { setState(() { selectedWeightUnit = value; }); + _onInputChanged(); + Navigator.pop(context); }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { - selectedWeightUnit = unit; - _onInputChanged(); + selectedWeightUnit = key; }); + _onInputChanged(); Navigator.pop(context); }), ), @@ -311,7 +328,7 @@ class _IdealBodyWeightWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ LocaleKeys.bodyFrameSize.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), - selectedBodyFrameSize.toText12(isBold: true, color: AppColors.textColor), + (_bodyFrameSizes[selectedBodyFrameSize] ?? selectedBodyFrameSize).tr().toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -321,7 +338,6 @@ class _IdealBodyWeightWidgetState extends State { SizedBox(width: 4.w), ], ).paddingSymmetrical(0.w, 16.w).onPress(() { - List _activity = ["Small (fingers overlaps)", "Medium (fingers touch)", "Large (fingers don't touch)"]; dialogService.showFamilyBottomSheetWithoutHWithChild( label: LocaleKeys.selectBodyFrameSize.tr(context: context), message: "", @@ -330,9 +346,10 @@ class _IdealBodyWeightWidgetState extends State { decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)), child: ListView.separated( shrinkWrap: true, - itemCount: _activity.length, + itemCount: _bodyFrameSizes.length, itemBuilder: (context, index) { - final unit = _activity[index]; + final key = _bodyFrameSizes.keys.elementAt(index); + final localeKey = _bodyFrameSizes[key]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -343,7 +360,7 @@ class _IdealBodyWeightWidgetState extends State { spacing: 8.h, children: [ Radio( - value: unit, + value: key, groupValue: selectedBodyFrameSize, activeColor: AppColors.errorColor, onChanged: (String? value) { @@ -351,17 +368,18 @@ class _IdealBodyWeightWidgetState extends State { setState(() { selectedBodyFrameSize = value; }); + _onInputChanged(); Navigator.pop(context); }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + localeKey.tr().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { - selectedBodyFrameSize = unit; - _onInputChanged(); + selectedBodyFrameSize = key; }); + _onInputChanged(); Navigator.pop(context); }), ), @@ -383,7 +401,7 @@ class _IdealBodyWeightWidgetState extends State { Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w), SizedBox(width: 12.w), Expanded( - child: "Calculates the ideal body weight based on height, weight and body size.".toText12(isBold: true, color: AppColors.inputLabelTextColor), + child: LocaleKeys.ibwAlert.tr().toText12(isBold: true, color: AppColors.inputLabelTextColor), ), ], ).paddingSymmetrical(0.w, 16.w), diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 89839f0d..d17ba896 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -27,6 +27,7 @@ import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; import 'package:hmg_patient_app_new/features/monthly_report/monthly_report_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; @@ -59,6 +60,7 @@ import 'package:hmg_patient_app_new/presentation/medical_file/widgets/lab_rad_ca import 'package:hmg_patient_app_new/presentation/medical_file/widgets/health_tracker_menu_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/health_tools_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_report_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_sick_leave_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_report/medical_reports_page.dart'; import 'package:hmg_patient_app_new/presentation/monthly_report/monthly_report.dart'; @@ -132,6 +134,8 @@ class _MedicalFilePageState extends State { medicalFileViewModel.setIsPatientSickLeaveListLoading(true); medicalFileViewModel.getPatientSickLeaveList(); medicalFileViewModel.onTabChanged(0); + medicalFileViewModel.setIsPatientMedicalReportsLoading(true); + medicalFileViewModel.getPatientMedicalReportList(); // Load vital signs hmgServicesViewModel.getPatientVitalSign(); } @@ -1285,65 +1289,78 @@ class _MedicalFilePageState extends State { ).paddingSymmetrical(0.w, 0.h); }), SizedBox(height: 16.h), - GridView( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - crossAxisSpacing: 10.h, - mainAxisSpacing: 16.w, - mainAxisExtent: 110.h, - ), - physics: NeverScrollableScrollPhysics(), - padding: EdgeInsets.zero, - shrinkWrap: true, - children: [ - // MedicalFileCard( - // label: LocaleKeys.monthlyReports.tr(context: context), - // textColor: AppColors.blackColor, - // backgroundColor: AppColors.whiteColor, - // svgIcon: AppAssets.monthly_reports_icon, - // isLargeText: true, - // iconSize: 36.h, - // ).onPress(() { - // monthlyReportViewModel.setHealthSummaryEnabled(cacheService.getBool(key: CacheConst.isMonthlyReportEnabled) ?? false); - // Navigator.of(context).push( - // CustomPageRoute( - // page: MonthlyReport(), - // ), - // ); - // }), - MedicalFileCard( - label: LocaleKeys.medicalReports.tr(context: context), - textColor: AppColors.blackColor, - backgroundColor: AppColors.whiteColor, - svgIcon: AppAssets.medical_reports_icon, - isLargeText: true, - iconSize: 36.w, - ).onPress(() { - medicalFileViewModel.setIsPatientMedicalReportsLoading(true); - medicalFileViewModel.getPatientMedicalReportList(); - Navigator.of(context).push( - CustomPageRoute( - page: MedicalReportsPage(), - ), - ); - }), - // MedicalFileCard( - // label: LocaleKeys.sickLeaveReport.tr(context: context), - // textColor: AppColors.blackColor, - // backgroundColor: AppColors.whiteColor, - // svgIcon: AppAssets.sick_leave_report_icon, - // isLargeText: true, - // iconSize: 36.h, - // ).onPress(() { - // Navigator.of(context).push( - // CustomPageRoute( - // page: PatientSickleavesListPage(), - // ), - // ); - // }), - ], - ).paddingSymmetrical(0.w, 0.0), - SizedBox(height: 24.h), + Selector listRequest, List listReady})>( + selector: (context, vm) => (isLoading: vm.isPatientMedicalReportsListLoading, listRequest: vm.patientMedicalReportRequestedList, listReady: vm.patientMedicalReportReadyList), + builder: (context, data, _){ + print("isloading are ${data.isLoading}"); + print("listRequest are ${data.listRequest}"); + print("listReady are ${data.listReady}"); + return MedicalReportCard( + isLoading: data.isLoading, listRequest: data.listRequest, listReady: data.listReady + ); + }, + ) + // GridView( + // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + // crossAxisCount: 3, + // crossAxisSpacing: 10.h, + // mainAxisSpacing: 16.w, + // mainAxisExtent: 110.h, + // ), + // physics: NeverScrollableScrollPhysics(), + // padding: EdgeInsets.zero, + // shrinkWrap: true, + // children: [ + // // MedicalFileCard( + // // label: LocaleKeys.monthlyReports.tr(context: context), + // // textColor: AppColors.blackColor, + // // backgroundColor: AppColors.whiteColor, + // // svgIcon: AppAssets.monthly_reports_icon, + // // isLargeText: true, + // // iconSize: 36.h, + // // ).onPress(() { + // // monthlyReportViewModel.setHealthSummaryEnabled(cacheService.getBool(key: CacheConst.isMonthlyReportEnabled) ?? false); + // // Navigator.of(context).push( + // // CustomPageRoute( + // // page: MonthlyReport(), + // // ), + // // ); + // // }), + // ///todo te changes of the medical report should be displayed here. + // /// + // + // // MedicalFileCard( + // // label: LocaleKeys.medicalReports.tr(context: context), + // // textColor: AppColors.blackColor, + // // backgroundColor: AppColors.whiteColor, + // // svgIcon: AppAssets.medical_reports_icon, + // // isLargeText: true, + // // iconSize: 36.w, + // // ).onPress(() { + // // + // // Navigator.of(context).push( + // // CustomPageRoute( + // // page: MedicalReportsPage(), + // // ), + // // ); + // // }), + // // MedicalFileCard( + // // label: LocaleKeys.sickLeaveReport.tr(context: context), + // // textColor: AppColors.blackColor, + // // backgroundColor: AppColors.whiteColor, + // // svgIcon: AppAssets.sick_leave_report_icon, + // // isLargeText: true, + // // iconSize: 36.h, + // // ).onPress(() { + // // Navigator.of(context).push( + // // CustomPageRoute( + // // page: PatientSickleavesListPage(), + // // ), + // // ); + // // }), + // ], + // ).paddingSymmetrical(0.w, 0.0), + ,SizedBox(height: 24.h), ], ); case 3: diff --git a/lib/presentation/medical_file/widgets/medical_report_card.dart b/lib/presentation/medical_file/widgets/medical_report_card.dart new file mode 100644 index 00000000..4759a65b --- /dev/null +++ b/lib/presentation/medical_file/widgets/medical_report_card.dart @@ -0,0 +1,101 @@ +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_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/medical_file/models/patient_medical_response_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/medical_report/medical_reports_page.dart'; +import 'package:hmg_patient_app_new/presentation/monthly_report/monthly_report.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/routes/custom_page_route.dart'; + +class MedicalReportCard extends StatelessWidget { + final bool isLoading; + final List listRequest; + final List listReady; + final VoidCallback? onViewDetails; + + const MedicalReportCard({ + super.key, + required this.isLoading, + required this.listRequest, + required this.listReady, + this.onViewDetails, + }); + + + + @override + Widget build(BuildContext context) { + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.medicalReport.tr().toText18( + isBold: true, + weight: FontWeight.w700, + ).toShimmer2(isShow: isLoading), + SizedBox(height: 12.h), + Wrap( + spacing: 8.w, + runSpacing: 8.h, + children: [ + if ( listRequest.isNotEmpty ) + AppCustomChipWidget( + labelText: '${listRequest.length.toString().padLeft(2, '0')} ${LocaleKeys.requested.tr()}', + icon: AppAssets.document, + iconColor: AppColors.alertColor, + textColor: AppColors.alertColor, + backgroundColor: AppColors.warningColorYellow.withOpacity(.2), + iconSize: 14.w, + ), + if (listReady.isNotEmpty) + AppCustomChipWidget( + labelText: '${listRequest.length.toString().padLeft(2, '0')} ${LocaleKeys.readyToDownload.tr()}', + icon: AppAssets.sucess, + iconColor: AppColors.successColor, + textColor: AppColors.successColor, + backgroundColor: AppColors.switchBackgroundColor, + iconSize: 14.w, + ), + ], + ).toShimmer2(isShow: isLoading), + SizedBox(height: 16.h), + CustomButton( + text: LocaleKeys.viewDetails.tr(), + icon: AppAssets.report, + borderWidth: 0, + isDisabled: false, + onPressed: (){ + Navigator.of(context).push( + CustomPageRoute( + page: MedicalReportsPage(), + ), + ); + }, + backgroundColor: AppColors.lightRedButtonColor, + borderColor: AppColors.lightRedButtonColor, + textColor: AppColors.primaryRedColor, + iconColor: AppColors.primaryRedColor, + fontSize: 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + height: 48.h, + ).toShimmer2(isShow: isLoading), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/prescriptions/prescription_item_view.dart b/lib/presentation/prescriptions/prescription_item_view.dart index 086b3f83..66ba13ed 100644 --- a/lib/presentation/prescriptions/prescription_item_view.dart +++ b/lib/presentation/prescriptions/prescription_item_view.dart @@ -59,7 +59,7 @@ class PrescriptionItemView extends StatelessWidget { runSpacing: 6.h, children: [ AppCustomChipWidget( - labelText: "${LocaleKeys.route.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].route}", + labelText: "${LocaleKeys.routePrescription.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].route}", ).toShimmer2(isShow: isLoading), AppCustomChipWidget( labelText: "${LocaleKeys.frequency.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].frequency}", From 256537cfb3d4cf4ac7fe39197792ca15809dda71 Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Wed, 15 Apr 2026 20:27:50 +0300 Subject: [PATCH 2/2] update and fix --- assets/langs/ar-SA.json | 14 +- assets/langs/en-US.json | 12 +- lib/core/api/api_client.dart | 1 + lib/core/api_consts.dart | 1 + lib/core/app_assets.dart | 2 +- lib/core/utils/push_notification_handler.dart | 127 ++++++++++++++++-- .../prescriptions_view_model.dart | 17 +++ lib/generated/locale_keys.g.dart | 8 ++ .../blood_donation/blood_donation_page.dart | 12 +- .../widgets/select_gender_widget.dart | 12 +- .../immediate_livecare_payment_details.dart | 1 + .../comprehensive_checkup_page.dart | 5 +- .../widgets/blood_cholesterol.dart | 4 +- .../widgets/triglycerides.dart | 8 +- .../hmg_services/services_page.dart | 90 ++++++------- .../smartwatches/smartwatch_home_page.dart | 2 +- lib/splashPage.dart | 59 +++++--- 17 files changed, 278 insertions(+), 97 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index a8df3819..96d967d4 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -1238,7 +1238,7 @@ "healthCalculatorsServices": "حاسبات\nالصحة", "healthConvertersServices": "محولات\nالصحة", "smartWatchesServices": "الساعات\nالذكية", - "exploreServices": "قائمة الخدمات او الخدمات", + "exploreServices": "تصفح الخدمات", "medicalAndCareServices": "خدمات الرعاية الطبية", "hmgServices": "خدمات مجموعة الحبيب الطبية", "personalServices": "الخدمات الشخصية", @@ -1471,7 +1471,7 @@ "covered": "مغطى", "vatPercent": "ضريبة القيمة المضافة (15%)", "proceedToPayment": "المتابعة للدفع", - "supportedSmartWatches": "الساعات الذكية المدعومة", + "supportedSmartWatches": "كيف تعمل؟", "pleaseMakeSureSamsungWatchConnected": "يرجى التأكد من أن ساعة Samsung الخاصة بك متصلة بهاتفك، ومتزامنة ومحدثة بشكل نشط.", "beforeSyncingDataFollowInstructions": "قبل مزامنة البيانات، يرجى التأكد من اتباع التعليمات بشكل صحيح.", "viewWatchInstructions": "عرض تعليمات الساعة", @@ -1768,6 +1768,12 @@ "locationServicesDisabled": "حالة الطقس غير متوفرة لتعطيل خدمات الموقع. يرجى تفعيل خدمات الموقع لتتمكن من رؤية معلومات الطقس.", "selectDateTimeKey": "اختيار", "medicalKey": "الطبية", - "convertBloodcholesterolInfo":"حول قيمة كوليسترول الدم بين ملجم/ديسيلتر و مليمول/لتر" - + "convertBloodcholesterolInfo":"حول قيمة كوليسترول الدم بين ملجم/ديسيلتر و مليمول/لتر", + "mldl": "مليجرام لكل ديسيلتر", + "mmol": "ملي مول لكل لتر", + "triglycerideInfo": "تغيير وحدات القياس بين مليجرام لكل ديسيلتر و ملي مول لكل لتر", + "selectSmartWatch": "تعيين", + "selectBloodDonationCity": "اختر المدينة", + "selectBloodDonationGender": "اختر الجنس", + "selectBloodType": "اختر الفصيلة" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index ee1c1d01..25a16577 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -1756,9 +1756,15 @@ "selectCriteria": "Select Criteria", "locationServicesDisabled": "Weather is unavailable because location services are disabled. Please enable location services to see the weather information.", "selectDateTimeKey": "Select", - "medicalKey": "Medical" -, - "convertBloodcholesterolInfo": "Convert blood cholesterol values between mg/dL and mmol/L. " + "medicalKey": "Medical", + "convertBloodcholesterolInfo": "Convert blood cholesterol values between mg/dL and mmol/L. ", + "mldl": "مليجرام لكل ديسيلتر", + "mmol": "ملي مول لكل لتر", + "triglycerideInfo": "Convert triglyceride values between mg/dL and mmol/L.", + "selectSmartWatch": "Select", + "selectBloodDonationCity": "Select City", + "selectBloodDonationGender": "Select Gender", + "selectBloodType": "Select Blood Type" } diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 374ac9d1..3c71281c 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -172,6 +172,7 @@ class ApiClientImp implements ApiClient { // body['VersionID'] = ApiConsts.appVersionID.toString(); if (!isExternal) { body['VersionID'] = ApiConsts.appVersionID.toString(); + if(!isRCService) body['Channel'] = ApiConsts.appChannelId.toString(); body['IPAdress'] = ApiConsts.appIpAddress; body['generalid'] = ApiConsts.appGeneralId; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 819691be..ff39fa70 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -189,6 +189,7 @@ class ApiConsts { static final createEReferral = "Services/Patients.svc/REST/CreateEReferral"; static final getEReferrals = "Services/Patients.svc/REST/GetEReferrals"; + //WATER CONSUMPTION static String h2oGetUserProgress = "Services/H2ORemainder.svc/REST/H2O_GetUserProgress"; static String h2oInsertUserActivity = "Services/H2ORemainder.svc/REST/H2O_InsertUserActivity"; diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index c3811a94..feb2c196 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -401,7 +401,7 @@ class AppAssets { static const String lockIcon = '$pngBasePath/lock-icon.png'; static const String dummyUser = '$pngBasePath/dummy_user.png'; static const String comprehensiveCheckupEn = '$pngBasePath/cc_en.png'; - static const String comprehensiveCheckupAr = '$pngBasePath/cc_er.png'; + static const String comprehensiveCheckupAr = '$pngBasePath/cc_ar.png'; static const String maleIcon = '$pngBasePath/male_icon.png'; static const String femaleIcon = '$pngBasePath/female_icon.png'; diff --git a/lib/core/utils/push_notification_handler.dart b/lib/core/utils/push_notification_handler.dart index 770961de..c82c608d 100644 --- a/lib/core/utils/push_notification_handler.dart +++ b/lib/core/utils/push_notification_handler.dart @@ -9,17 +9,27 @@ import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_callkit_incoming/entities/android_params.dart'; +import 'package:flutter_callkit_incoming/entities/call_event.dart'; import 'package:flutter_callkit_incoming/entities/call_kit_params.dart'; import 'package:flutter_callkit_incoming/entities/ios_params.dart'; import 'package:flutter_callkit_incoming/entities/notification_params.dart'; import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; import 'package:flutter_ios_voip_kit_karmm/call_state_type.dart'; import 'package:flutter_ios_voip_kit_karmm/flutter_ios_voip_kit.dart'; +import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/cache_consts.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:uuid/uuid.dart'; +// Global tracking variables for call state +bool _isCallInProgress = false; +String? _currentCallId; +Timer? _callTimeoutTimer; +String? _currentSessionId; // Store session ID for API call + // |--> Push Notification Background @pragma('vm:entry-point') Future backgroundMessageHandler(dynamic message) async { @@ -39,34 +49,36 @@ callPage(String sessionID, String token) async {} _incomingCall(Map data) async { print('the value of the _incomingCall remote message is $data'); - // LandingPage.incomingCallData = IncomingCallData.fromJson(data); - // var dataItem = await AppSharedPreferences().getObject('call_data'); - // if(dataItem != null ) return; // to stop repeated attempt to invoke the call - // if (LandingPage.isOpenCallPage == false) { - // LandingPage.isOpenCallPage = true; + // Check if there's already a call in progress to prevent duplicates + if (_isCallInProgress && _currentCallId != null) { + print('⚠️ Call already in progress (ID: $_currentCallId), ignoring duplicate notification'); + return; + } String roomID = data['session_id'] ?? ''; String callTypeID = data['AppointmentNo'] ?? ''; - // GetIt.instance().saveString(key: CacheConst.zoomRoomID, value: roomID); - // GetIt.instance().saveBool(key: CacheConst.isAppOpenedFromCall, value: true); + // Generate unique call ID + var _currentUuid = Uuid().v4(); + _currentCallId = _currentUuid; + _isCallInProgress = true; + _currentSessionId = roomID; // Store session ID for decline API call + + print('📞 Starting new call with ID: $_currentCallId, Session: $_currentSessionId'); await Utils.saveStringFromPrefs(CacheConst.zoomRoomID, roomID); await Utils.saveStringFromPrefs(CacheConst.callTypeID, callTypeID); await Utils.saveBoolFromPrefs(CacheConst.isAppOpenedFromCall, true); - WidgetsFlutterBinding.ensureInitialized(); int callKitType = callTypeID == '2' ? 0 : 1; - var _currentUuid = Uuid().v4(); CallKitParams callKitParams = CallKitParams( id: _currentUuid, nameCaller: "Dr Al Habib", appName: 'Dr Al Habib', avatar: 'https://play-lh.googleusercontent.com/FBNNpxb7m6eM6wtW7MV1Ffp6OXOGLI38q47zcvP29OCYA1yhYH5mZzl5itZi0TgOyZpG', - // handle: LandingPage.incomingCallData.name, type: callKitType, textAccept: 'Accept', textDecline: 'Decline', @@ -85,8 +97,8 @@ _incomingCall(Map data) async { duration: 30000, extra: { 'userId': '1a2b3c4d', - 'callTypeID': callTypeID, // Store actual callTypeID here - 'roomID': roomID, // Store roomID here for backup + 'callTypeID': callTypeID, + 'roomID': roomID, }, headers: {'apiKey': 'Abc@123!', 'platform': 'flutter'}, android: const AndroidParams( @@ -96,10 +108,10 @@ _incomingCall(Map data) async { isShowLogo: false, logoUrl: "https://play-lh.googleusercontent.com/FBNNpxb7m6eM6wtW7MV1Ffp6OXOGLI38q47zcvP29OCYA1yhYH5mZzl5itZi0TgOyZpG", ringtonePath: 'system_ringtone_default', - backgroundColor: '#ED1C2B', + backgroundColor: '#2E3039', backgroundUrl: 'https://saudipedia.com/en/saudipediaen/uploads/images/2024/08/02/97358.jpg', actionColor: '#4CAF50', - textColor: '#000000', + textColor: '#FFFFFF', incomingCallNotificationChannelName: "Incoming Call", missedCallNotificationChannelName: "Missed Call", isShowCallID: false), @@ -120,12 +132,89 @@ _incomingCall(Map data) async { ringtonePath: 'system_ringtone_default', ), ); + FlutterCallkitIncoming.unsilenceEvents(); await FlutterCallkitIncoming.showCallkitIncoming(callKitParams); + // Start 60-second timeout timer to auto-dismiss call + _callTimeoutTimer?.cancel(); // Cancel any existing timer + _callTimeoutTimer = Timer(Duration(seconds: 40), () async { + print('⏱️ Call timeout (60 seconds) - auto dismissing call notification'); + await _endCallAndCleanup(_currentUuid); + }); + await Future.delayed(Duration(milliseconds: 500)); } +// Helper method to end call and cleanup +Future _endCallAndCleanup(String callId, {bool isDeclined = false}) async { + try { + print('🧹 Cleaning up call: $callId'); + + // If call was declined, update session status via API + if (isDeclined && _currentSessionId != null && _currentSessionId!.isNotEmpty) { + await _updateSessionStatus(_currentSessionId!, 3, 'Patient'); + } + + // End the specific call + await FlutterCallkitIncoming.endCall(callId); + + // Clear all calls to ensure clean state + await FlutterCallkitIncoming.endAllCalls(); + + // Reset tracking flags + _isCallInProgress = false; + _currentCallId = null; + _currentSessionId = null; + + // Cancel timer + _callTimeoutTimer?.cancel(); + _callTimeoutTimer = null; + + // Clear cache + await Utils.saveBoolFromPrefs(CacheConst.isAppOpenedFromCall, false); + await Utils.saveStringFromPrefs(CacheConst.zoomRoomID, ''); + await Utils.saveStringFromPrefs(CacheConst.callTypeID, ''); + + print('✅ Call and push notification cleanup completed successfully'); + } catch (e) { + print('❌ Error during call cleanup: $e'); + // Force reset flags even if there's an error + _isCallInProgress = false; + _currentCallId = null; + _currentSessionId = null; + _callTimeoutTimer?.cancel(); + _callTimeoutTimer = null; + } +} + +// Helper method to update session status via API +Future _updateSessionStatus(String sessionId, int sessionStatus, String sessionEndedBy) async { + try { + + final apiClient = getIt.get(); + + Map requestBody = { + "Open_SessionID": sessionId, + "SessionStatus": sessionStatus, + "SessionEndedBy": sessionEndedBy + }; + + await apiClient.post( + CHANGE_PATIENT_ER_SESSION, + body: requestBody, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + print('✅ Session status updated successfully: $response'); + }, + onFailure: (error, statusCode, {messageStatus, failureType}) { + print('❌ Failed to update session status: $error'); + }, + ); + } catch (e) { + print('❌ Exception updating session status: $e'); + } +} + Future openCallPage(BuildContext context) async { try { // if (incomingCallData!.background == "0") { @@ -291,6 +380,10 @@ class PushNotificationHandler { try { print('🎈 example: onDidRejectIncomingCall $uuid - $callerId'); timeOutTimer.cancel(); + // Cleanup on reject with API call + if (_currentCallId != null) { + await _endCallAndCleanup(_currentCallId!, isDeclined: true); + } } catch (err) {} }; @@ -301,6 +394,12 @@ class PushNotificationHandler { print('🎈 example: onDidAcceptIncomingCall $uuid - $callerId'); await voIPKit.acceptIncomingCall(callerState: CallStateType.calling); await voIPKit.callConnected(); + + // Cleanup on accept - cancel timer and reset flags + _callTimeoutTimer?.cancel(); + _callTimeoutTimer = null; + _isCallInProgress = false; + _currentCallId = null; await Future.delayed(Duration(seconds: 2)).then((val) async { await voIPKit.endCall(); }); diff --git a/lib/features/prescriptions/prescriptions_view_model.dart b/lib/features/prescriptions/prescriptions_view_model.dart index 0f09f1e9..c374b3e2 100644 --- a/lib/features/prescriptions/prescriptions_view_model.dart +++ b/lib/features/prescriptions/prescriptions_view_model.dart @@ -306,6 +306,23 @@ class PrescriptionsViewModel extends ChangeNotifier { } else if (apiResponse.messageStatus == 1) { isPrescriptionsDeliveryOrdersLoading = false; prescriptionsOrderList = apiResponse.data!; + + // Sort by created date (newest first) + prescriptionsOrderList.sort((a, b) { + try { + if (a.created == null && b.created == null) return 0; + if (a.created == null) return 1; + if (b.created == null) return -1; + + DateTime dateA = DateTime.parse(a.created!); + DateTime dateB = DateTime.parse(b.created!); + return dateB.compareTo(dateA); // Descending order (newest first) + } catch (e) { + print('Error parsing date for sorting: $e'); + return 0; + } + }); + notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index c7c924ef..49bd90b9 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1759,5 +1759,13 @@ abstract class LocaleKeys { static const locationServicesDisabled = 'locationServicesDisabled'; static const selectDateTimeKey = 'selectDateTimeKey'; static const medicalKey = 'medicalKey'; + static const convertBloodcholesterolInfo = 'convertBloodcholesterolInfo'; + static const mldl = 'mldl'; + static const mmol = 'mmol'; + static const triglycerideInfo = 'triglycerideInfo'; + static const selectSmartWatch = 'selectSmartWatch'; + static const selectBloodDonationCity = 'selectBloodDonationCity'; + static const selectBloodDonationGender = 'selectBloodDonationGender'; + static const selectBloodType = 'selectBloodType'; } diff --git a/lib/presentation/blood_donation/blood_donation_page.dart b/lib/presentation/blood_donation/blood_donation_page.dart index c6767c2d..4a8f91a6 100644 --- a/lib/presentation/blood_donation/blood_donation_page.dart +++ b/lib/presentation/blood_donation/blood_donation_page.dart @@ -148,8 +148,8 @@ class _BloodDonationPageState extends State { children: [ LocaleKeys.city.tr().toText16(color: AppColors.textColor, isBold: true), (appState.isArabic() - ? (bloodDonationVM.selectedCity?.descriptionN ?? LocaleKeys.select.tr()) - : bloodDonationVM.selectedCity?.description ?? LocaleKeys.select.tr(context: context)) + ? (bloodDonationVM.selectedCity?.descriptionN ?? LocaleKeys.selectBloodDonationCity.tr()) + : bloodDonationVM.selectedCity?.description ?? LocaleKeys.selectBloodDonationCity.tr(context: context)) .toText14(color: AppColors.greyTextColor, isBold: true), ], ), @@ -176,8 +176,8 @@ class _BloodDonationPageState extends State { children: [ LocaleKeys.gender.tr().toText16(color: AppColors.textColor, isBold: true), (appState.isArabic() - ? (bloodDonationVM.selectedGender?.typeAr ?? LocaleKeys.select.tr()) - : bloodDonationVM.selectedGender?.type ?? LocaleKeys.select.tr(context: context)) + ? (bloodDonationVM.selectedGender?.typeAr ?? LocaleKeys.selectBloodDonationGender.tr()) + : bloodDonationVM.selectedGender?.type ?? LocaleKeys.selectBloodDonationGender.tr(context: context)) .toText14(color: AppColors.greyTextColor, isBold: true), ], ), @@ -208,8 +208,8 @@ class _BloodDonationPageState extends State { LocaleKeys.bloodType.tr().toText16(color: AppColors.textColor, isBold: true), // bloodDonationVM.selectedBloodType?.toText14(color: AppColors.greyTextColor, isBold: true), (appState.isArabic() - ? (bloodDonationVM.selectedBloodType ?? LocaleKeys.select.tr()) - : bloodDonationVM.selectedBloodType ?? LocaleKeys.select.tr(context: context)) + ? (bloodDonationVM.selectedBloodType ?? LocaleKeys.selectBloodType.tr()) + : bloodDonationVM.selectedBloodType ?? LocaleKeys.selectBloodType.tr(context: context)) .toText14(color: AppColors.greyTextColor, isBold: true) ], ), diff --git a/lib/presentation/blood_donation/widgets/select_gender_widget.dart b/lib/presentation/blood_donation/widgets/select_gender_widget.dart index 0d360a80..1368c4e3 100644 --- a/lib/presentation/blood_donation/widgets/select_gender_widget.dart +++ b/lib/presentation/blood_donation/widgets/select_gender_widget.dart @@ -37,7 +37,12 @@ class SelectGenderWidget extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: 8.h, - children: [GenderTypeEnum.values[index].name.toCamelCase.toText16(color: AppColors.textColor, isBold: true)], + children: [ + (isArabic + ? GenderTypeEnum.values[index].typeAr + : GenderTypeEnum.values[index].type) + .toText16(color: AppColors.textColor, isBold: true) + ], ), ), Transform.flip( @@ -52,7 +57,10 @@ class SelectGenderWidget extends StatelessWidget { ), ], ).paddingSymmetrical(16.h, 16.h).onPress(() { - bloodDonationViewModel.onGenderChange(GenderTypeEnum.values[index].name.toCamelCase); + final genderValue = isArabic + ? GenderTypeEnum.values[index].typeAr + : GenderTypeEnum.values[index].type; + bloodDonationViewModel.onGenderChange(genderValue); Navigator.of(context).pop(); })); }, diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart index 1228e1e4..b3801113 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart @@ -240,6 +240,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { ], ).paddingSymmetrical(24.h, 0.h), (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0" || immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0.0") + // (true) ? CustomButton( text: LocaleKeys.confirmLiveCare.tr(context: context), onPressed: () async { diff --git a/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart index 589764c1..0761ab6c 100644 --- a/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart +++ b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart @@ -250,7 +250,10 @@ class _ComprehensiveCheckupPageState extends State { physics: NeverScrollableScrollPhysics(), itemBuilder: (context, index) { final service = services[index]; - final serviceName = service.text ?? service.textN ?? ''; + final appStateLocal = getIt.get(); + final serviceName = appStateLocal.isArabic() + ? (service.textN ?? service.text ?? '') + : (service.text ?? service.textN ?? ''); final price = service.priceTotal ?? 0.0; return RadioListTileWidget( value: service.iD ?? 0, diff --git a/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart b/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart index d24463bf..14b956e0 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart @@ -78,7 +78,7 @@ class _BloodCholesterolWidgetState extends State { child: Column( children: [ _buildInputField( - label: "MG/DL", + label: LocaleKeys.mldl.tr(), hint: "120", controller: _mgdlController, focusNode: _mgdlFocus, @@ -107,7 +107,7 @@ class _BloodCholesterolWidgetState extends State { ], ), _buildInputField( - label: "MMOL/L", + label:LocaleKeys.mmol.tr(), hint: "3.1", controller: _mmolController, focusNode: _mmolFocus, diff --git a/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart b/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart index b61eaaed..dd5eb0f0 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart @@ -1,4 +1,6 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; @@ -80,7 +82,7 @@ class _TriglyceridesWidgetState extends State { child: Column( children: [ _buildInputField( - label: "MG/DL", + label: LocaleKeys.mldl.tr(), hint: "150", controller: _mgdlController, focusNode: _mgdlFocus, @@ -111,7 +113,7 @@ class _TriglyceridesWidgetState extends State { ), _buildInputField( - label: "MMOL/L", + label: LocaleKeys.mmol, hint: "1.7", controller: _mmolController, focusNode: _mmolFocus, @@ -134,7 +136,7 @@ class _TriglyceridesWidgetState extends State { SizedBox(width: 12.w), Expanded( child: - "Convert triglyceride values between mg/dL and mmol/L." + LocaleKeys.triglycerideInfo.tr() .toText12( isBold: true, color: AppColors.inputLabelTextColor, diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart index 734401fe..af6672ea 100644 --- a/lib/presentation/hmg_services/services_page.dart +++ b/lib/presentation/hmg_services/services_page.dart @@ -105,51 +105,51 @@ class _ServicesPageState extends State { ); } }), - HmgServicesComponentModel( - 11, - LocaleKeys.indoorNavigation.tr(), - "", - AppAssets.indoor_nav_icon, - bgColor: Color(0xff45A2F8), - true, - route: null, - onTap: () { - showCommonBottomSheetWithoutHeight( - title: LocaleKeys.selectHospital.tr(), - GetIt.instance().navigatorKey.currentContext!, - child: ChangeNotifierProvider( - create: (context) => HospitalSelectionBottomSheetViewModel(getIt()), - child: Consumer( - builder: (_, vm, __) => HospitalBottomSheetBody( - searchText: vm.searchController, - displayList: vm.displayList, - onFacilityClicked: (value) { - vm.setSelectedFacility(value); - vm.getDisplayList(); - }, - onHospitalClicked: (hospital) { - Navigator.pop(GetIt.instance().navigatorKey.currentContext!); - vm.openPenguin(hospital); - }, - onHospitalSearch: (value) { - vm.searchHospitals(value ?? ""); - }, - selectedFacility: vm.selectedFacility, - hmcCount: vm.hmcCount, - hmgCount: vm.hmgCount, - ), - ), - ), - isFullScreen: false, - isCloseButtonVisible: true, - hasBottomPadding: false, - backgroundColor: AppColors.bottomSheetBgColor, - callBackFunc: () { - GetIt.instance().navigatorKey.currentContext!.read().clearSearchText(); - }, - ); - }, - ), + // HmgServicesComponentModel( + // 11, + // LocaleKeys.indoorNavigation.tr(), + // "", + // AppAssets.indoor_nav_icon, + // bgColor: Color(0xff45A2F8), + // true, + // route: null, + // onTap: () { + // showCommonBottomSheetWithoutHeight( + // title: LocaleKeys.selectHospital.tr(), + // GetIt.instance().navigatorKey.currentContext!, + // child: ChangeNotifierProvider( + // create: (context) => HospitalSelectionBottomSheetViewModel(getIt()), + // child: Consumer( + // builder: (_, vm, __) => HospitalBottomSheetBody( + // searchText: vm.searchController, + // displayList: vm.displayList, + // onFacilityClicked: (value) { + // vm.setSelectedFacility(value); + // vm.getDisplayList(); + // }, + // onHospitalClicked: (hospital) { + // Navigator.pop(GetIt.instance().navigatorKey.currentContext!); + // vm.openPenguin(hospital); + // }, + // onHospitalSearch: (value) { + // vm.searchHospitals(value ?? ""); + // }, + // selectedFacility: vm.selectedFacility, + // hmcCount: vm.hmcCount, + // hmgCount: vm.hmgCount, + // ), + // ), + // ), + // isFullScreen: false, + // isCloseButtonVisible: true, + // hasBottomPadding: false, + // backgroundColor: AppColors.bottomSheetBgColor, + // callBackFunc: () { + // GetIt.instance().navigatorKey.currentContext!.read().clearSearchText(); + // }, + // ); + // }, + // ), HmgServicesComponentModel(11, LocaleKeys.eReferralServices.tr(), "", AppAssets.eReferral, bgColor: AppColors.eReferralCardColor, true, route: null, onTap: () async { if (getIt.get().isAuthenticated) { getIt.get().pushPageRoute(AppRoutes.eReferralPage); diff --git a/lib/presentation/smartwatches/smartwatch_home_page.dart b/lib/presentation/smartwatches/smartwatch_home_page.dart index b6c98ee9..53e562e6 100644 --- a/lib/presentation/smartwatches/smartwatch_home_page.dart +++ b/lib/presentation/smartwatches/smartwatch_home_page.dart @@ -85,7 +85,7 @@ class SmartwatchHomePage extends StatelessWidget { Image.asset("assets/images/png/smartwatches/apple-watch-5.jpg", width: 136.w, height: 136.h).paddingSymmetrical(24.w, 8.h), "Apple Watch".needTranslation.toText16(isBold: true), CustomButton( - text: LocaleKeys.select.tr(context: context), + text: LocaleKeys.selectSmartWatch.tr(context: context), onPressed: () { context.read().setSelectedWatchType(SmartWatchTypes.apple, "assets/images/png/smartwatches/apple-watch-5.jpg"); getIt.get().pushPage(page: SmartwatchInstructionsPage( diff --git a/lib/splashPage.dart b/lib/splashPage.dart index c7be036f..7fe97e6f 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -5,6 +5,7 @@ import 'package:flutter_callkit_incoming/entities/call_event.dart'; import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; import 'package:flutter_zoom_videosdk/native/zoom_videosdk.dart'; import 'package:get_it/get_it.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; @@ -182,9 +183,22 @@ class _SplashScreenState extends State { } break; case Event.actionCallDecline: - print('actionCallConnected Event Received: ------->'); + print('actionCallDecline Event Received: ------->'); + + // Get session ID from cache + String sessionId = await Utils.getStringFromPrefs(CacheConst.zoomRoomID); + // + // // Update session status to 3 (declined by patient) + // if (sessionId.isNotEmpty) { + await _updateSessionStatus(sessionId, 3, 'Patient'); + // } + await FlutterCallkitIncoming.endAllCalls(); GetIt.instance().remove(key: CacheConst.isAppOpenedFromCall); + + // Clear cache + await Utils.saveStringFromPrefs(CacheConst.zoomRoomID, ''); + await Utils.saveStringFromPrefs(CacheConst.callTypeID, ''); break; case Event.actionCallEnded: break; @@ -220,20 +234,35 @@ class _SplashScreenState extends State { /// load the Privilege from service Future loadPrivilege() async { - // ProjectViewModel projectProvider = Provider.of(context, listen: false); - // projectProvider.setPrivilegeModelList(privilege: _privilegeService.privilegeModelList); - // projectProvider.setVidaPlusProjectList(_privilegeService.vidaPlusProjectListModel); - // projectProvider.setHMCProjectList(_privilegeService.hMCProjectListModel); - // projectProvider.setProjectsDetailList(_privilegeService.projectDetailListModel); - // double lat = await AppSharedPreferences().getDouble(USER_LAT) ?? 0.0; - // double long = await AppSharedPreferences().getDouble(USER_LONG) ?? 0.0; - // AppSharedPreferences().clear(); // Clearing Shared Preferences On App Launch - // await AppSharedPreferences().setDouble(USER_LAT, lat); - // await AppSharedPreferences().setDouble(USER_LONG, long); - // AppSharedPreferences().setString(APP_LANGUAGE, projectProvider.isArabic ? "ar" : "en"); - // var themeNotifier = Provider.of(context, listen: false); - // themeNotifier.setTheme(defaultTheme(fontName: projectProvider.isArabic ? 'Cairo' : 'Poppins')); - // PushNotificationHandler().init(context); // Asyncronously + // ...existing code... + } + + /// Update session status when call is declined + Future _updateSessionStatus(String sessionId, int sessionStatus, String sessionEndedBy) async { + try { + print('📡 Updating session status - SessionID: $sessionId, Status: $sessionStatus, EndedBy: $sessionEndedBy'); + + final apiClient = getIt.get(); + String sessionID = await Utils.getStringFromPrefs(CacheConst.zoomRoomID); + Map requestBody = { + "Open_SessionID": sessionID, + "SessionStatus": sessionStatus, + "SessionEndedBy": sessionEndedBy + }; + + await apiClient.post( + CHANGE_PATIENT_ER_SESSION, + body: requestBody, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + print('✅ Session status updated successfully: $response'); + }, + onFailure: (error, statusCode, {messageStatus, failureType}) { + print('❌ Failed to update session status: $error'); + }, + ); + } catch (e) { + print('❌ Exception updating session status: $e'); + } } @override