diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index b95b6709..28aab087 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -1245,7 +1245,7 @@ "healthCalculatorsServices": "حاسبات\nالصحة", "healthConvertersServices": "محولات\nالصحة", "smartWatchesServices": "الساعات\nالذكية", - "exploreServices": "قائمة الخدمات او الخدمات", + "exploreServices": "تصفح الخدمات", "medicalAndCareServices": "خدمات الرعاية الطبية", "hmgServices": "خدمات مجموعة الحبيب الطبية", "personalServices": "الخدمات الشخصية", @@ -1479,7 +1479,7 @@ "covered": "مغطى", "vatPercent": "ضريبة القيمة المضافة (15%)", "proceedToPayment": "المتابعة للدفع", - "supportedSmartWatches": "الساعات الذكية المدعومة", + "supportedSmartWatches": "كيف تعمل؟", "pleaseMakeSureSamsungWatchConnected": "يرجى التأكد من أن ساعة Samsung الخاصة بك متصلة بهاتفك، ومتزامنة ومحدثة بشكل نشط.", "beforeSyncingDataFollowInstructions": "قبل مزامنة البيانات، يرجى التأكد من اتباع التعليمات بشكل صحيح.", "viewWatchInstructions": "عرض تعليمات الساعة", @@ -1776,7 +1776,6 @@ "locationServicesDisabled": "حالة الطقس غير متوفرة لتعطيل خدمات الموقع. يرجى تفعيل خدمات الموقع لتتمكن من رؤية معلومات الطقس.", "selectDateTimeKey": "اختيار", "medicalKey": "الطبية", - "convertBloodcholesterolInfo":"حول قيمة كوليسترول الدم بين ملجم/ديسيلتر و مليمول/لتر", "carbsAlert": "حساب نسب المغذيات (الكربوهيدرات ،البروتين والدهون) بالسعرات الحرارية والقرامات وفقا لنسب محددة مسبقا", "bodyFatAlert": "يحسب بناءً على الحجم", "ibwAlert": "حساب الوزن المثالي بناءً على الطول، الوزن، وبنية الجسم", @@ -1785,5 +1784,12 @@ "routePrescription": "طريقة الاستخدام", "noNearestER": "لا يوجد طوارئ قريب منك", "loginToUseServiceLiveCare": "تحتاج إلى تسجيل الدخول لاستخدام هذه الخدمة" - + "convertBloodcholesterolInfo":"حول قيمة كوليسترول الدم بين ملجم/ديسيلتر و مليمول/لتر", + "mldl": "مليجرام لكل ديسيلتر", + "mmol": "ملي مول لكل لتر", + "triglycerideInfo": "تغيير وحدات القياس بين مليجرام لكل ديسيلتر و ملي مول لكل لتر", + "selectSmartWatch": "تعيين", + "selectBloodDonationCity": "اختر المدينة", + "selectBloodDonationGender": "اختر الجنس", + "selectBloodType": "اختر الفصيلة" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index d0900bd8..9564b6b2 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -1764,9 +1764,6 @@ "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. ", "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.", @@ -1774,8 +1771,16 @@ "selectBloodGroup": "Select Blood Group", "routePrescription": "Route", "noNearestER": "No nearest Er Around you", - "loginToUseServiceLiveCare": "Log In to Use This Service" - + "loginToUseServiceLiveCare": "Log In to Use This Service", + "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 7800d244..107e06d1 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -404,7 +404,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 581f1ea0..38b1cead 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1776,5 +1776,12 @@ abstract class LocaleKeys { static const routePrescription = 'routePrescription'; static const noNearestER = 'noNearestER'; static const loginToUseServiceLiveCare = 'loginToUseServiceLiveCare'; + 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 4a2a7cc4..b706d2cd 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 fc8863d8..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].displayName.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 07e26b6f..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 = getIt.get().isArabic()? service.textN??service.text??"" : 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