Merge pull request 'dev_sultan' (#241) from dev_sultan into master

Reviewed-on: #241
pull/243/head
khansultan1 1 day ago
commit 81cdb4be30

@ -198,6 +198,7 @@
"enterEmail": "يرجى إدخال البريد الإلكتروني",
"family": "عائلتي",
"familyTitle": "ملفات عائلتي",
"familyTitle2": "ملفات العائلة",
"myFamily": "عائلتي",
"addNewMember": "إضافة فرد جديد للعائلة",
"sentRequests": "الطلبات المرسلة",
@ -218,7 +219,7 @@
"unit": "الوحدة",
"request": "طلب",
"memberName": "اسم العضو",
"switchLogin": "تبديل",
"switchLogin": "عرض",
"removeMember": "إزالة",
"allowView": "السماح",
"rejectView": "رفض",
@ -488,7 +489,7 @@
"shareReview": "شارك التقييم",
"review": " التقييمات",
"viewMedicalFile": "عرض التفاصيل",
"viewAllServices": "عرض جميع الخدمات",
"viewAllServices": "جميع الخدمات",
"medicalFile": "الملف الطبي",
"verified": "تم التحقق",
"checkup": "فحص",
@ -930,7 +931,7 @@
"vat15": "ضريبة القيمة المضافة 15%",
"general": "عام",
"liveCare": "لايف كير",
"recentVisits": "الزيارات الأخيرة",
"recentVisits": "قائمة الزيارات",
"favouriteDoctors": "الأطباء المفضلون",
"searchByClinic": "العيادة",
"tapToSelectClinic": "انقر لاختيار العيادة",
@ -951,7 +952,7 @@
"freeMedicineDelivery": "توصيل مجاني للأدوية",
"offersFreeMedicineDelivery": "يوفر توصيل مجاني للأدوية لموعد لايف كير",
"dentalChiefComplaints": "الشكاوى الرئيسة للأسنان",
"viewAvailableAppointments": "عرض المواعيد المتاحة",
"viewAvailableAppointments": "المواعيد المتاحة",
"doctorProfile": " ملف الطبيب",
"waitingAppointment": "موعد الانتظار",
"hospitalInformation": "معلومات المستشفى",
@ -1239,6 +1240,7 @@
"hmgServices": "خدمات مجموعة الحبيب الطبية",
"personalServices": "الخدمات الشخصية",
"habibWallet": "محفظة الحبيب",
"habibWallet2": "المحفظة",
"loginToViewWalletBalance": "سجل الدخول لعرض رصيد محفظتك",
"recharge": "إعادة الشحن",
"loginToViewMedicalFile": "سجل الدخول لعرض ملفك الطبي",
@ -1297,7 +1299,7 @@
"referenceRange": "النطاق المرجعي",
"downloadReport": "تحميل التقارير",
"generatingReport": "جارٍ إنشاء التقرير، يرجى الانتظار...",
"noLabResults": يس لديك أي نتائج مختبرية حتى الآن.",
"noLabResults": ا يوجد نتائج",
"labResultDetails": "تفاصيل نتائج المختبر",
"resultOf": "نتيجة",
"whatIsThisResult": "ما هي هذه النتيجة؟",
@ -1471,7 +1473,7 @@
"beforeSyncingDataFollowInstructions": "قبل مزامنة البيانات، يرجى التأكد من اتباع التعليمات بشكل صحيح.",
"viewWatchInstructions": "عرض تعليمات الساعة",
"healthConnectAppNotInstalled": "يبدو أنه ليس لديك تطبيق Health Connect مثبتًا. يرجى تثبيته من متجر Play لمزامنة بيانات صحتك.",
"setTimerOfReminder": "ضبط مؤقت التذكير",
"setTimerOfReminder": "ضبط التذكير",
"youHaveAppointmentWithDr": "لديك موعد مع د. ",
"hours": "ساعات",
"minutes": "دقائق",
@ -1536,7 +1538,7 @@
"notifyMeBeforeConsumptionTime": "أبلغني قبل وقت الاستهلاك",
"noMedicationsToday": "لا أدوية اليوم",
"reminders": "تذكيرات",
"reminderAddedToCalendar": "تمت إضافة تذكير إلى التقويم ",
"reminderAddedToCalendar": "تمت إضافة تذكير إلى التقويم ",
"errorWhileSettingCalendar": "حدث خطأ أثناء ضبط التقويم:{error}",
"instructions": "التعليمات",
"requests": "الطلبات",
@ -1562,14 +1564,14 @@
"bmi": "مؤشر كتلة الجسم",
"yesPleaseINeedHelp": "نعم، من فضلك أريد المساعدة",
"aiOverView": "AI ملخص",
"generateAiAnalysis": "قم بإجراء تحليل لهذا الطلب AI",
"generateAiAnalysis": "التحليل باستخدام الذكاء الاصطناعي",
"aiAnalysis": "AI تحليل",
"prediction": "تنبؤ",
"prediction": "التوقعات",
"prevention": "الوقاية",
"disclaimer": "تنبيه",
"thisAboveInfo": "المعلومات المذكورة أعلاه مُولّدة بواسطة AI بناءً على نتائج تحاليلك المخبرية، وهي لأغراض إعلامية فقط. لا ينبغي اعتبارها تشخيصًا طبيًا أو خطة علاجية. استشر طبيبك دائمًا للحصول على نصيحة طبية مُخصصة قبل اتخاذ أي قرارات تتعلق بصحتك.",
"aiDisclaimer": "سيتم مشاركة بيانات نتائج تحاليلك المخبرية بشكل آمن مع خدمة الذكاء الاصطناعي لدينا لتحليلها. يساعد هذا في تقديم رؤى صحية مُخصصة. هل ترغب في المتابعة؟",
"consent": "موافقة",
"consent": "نتائج المختبر",
"bmiNormalDescription": "مؤشر كتلة جسمك ضمن المعدل الصحي. استمر في اتباع نظام غذائي متوازن، وممارسة النشاط البدني بانتظام، وإجراء الفحوصات الدورية للحفاظ على صحة جيدة. راقب أي تغييرات ملحوظة مع مرور الوقت.",
"bmiUnderweightDescription": "يشير مؤشر كتلة الجسم إلى أنك تعاني من نقص الوزن. لذا، يُنصح بزيادة السعرات الحرارية المتناولة من خلال تناول الأطعمة الغنية بالعناصر الغذائية، وممارسة تمارين المقاومة لبناء العضلات، واستشارة أخصائي رعاية صحية للتحقق من وجود أسباب كامنة.",
"bmiOverweightDescription": "يشير مؤشر كتلة الجسم إلى زيادة الوزن. لذا، يُنصح بالسعي إلى إنقاص الوزن تدريجياً وبشكل مستدام من خلال تقليل السعرات الحرارية بشكل معتدل، وممارسة النشاط البدني بانتظام، واختيار الأطعمة الصحية. استشر طبيبك قبل إجراء أي تغييرات جذرية في نمط حياتك.",
@ -1645,7 +1647,7 @@
"insuranceRequestSubmittedSuccessfully": "تم إرسال طلب تحديث بيانات التأمين بنجاح. سيتم إعلامك بمجرد الانتهاء.",
"updatingEmailAddress": "جارٍ تحديث عنوان البريد الإلكتروني، يرجى الانتظار...",
"verifyInsurance": "التحقق من التأمين",
"tests": "تحليل",
"tests": "تحاليل",
"calendarPermissionAlert": "يرجى منح إذن الوصول إلى التقويم من إعدادات التطبيق لضبط تذكير تناول الدواء.",
"sortByNearestLocation": "حسب الأقرب الى موقعك",
"giveLocationPermissionForNearestList": "يرجى منح إذن الوصول إلى الموقع من إعدادات التطبيق لعرض أقرب المواقع.",
@ -1673,7 +1675,7 @@
"homeHealthCareServiceHeader": "صحتك، في أبهى صورها. رعاية فائقة الجودة، تصلك إلى عتبة دارك.",
"homeHealthCareServiceDescription": "نقدم لكم رعاية صحية عالية الجودة تصلكم إلى عتبة منزلكم. ممرضات ذوات خبرةممرضات ذوات خبرة يقدمون لكم الرعاية اللازمة.",
"profileOnlyText": "الملف الشخصي",
"information": "معلومة",
"information": "معلومات الطبيب",
"noFavouriteDoctors": "ليس لديك أي قائمة مفضلة حتى الآن",
"addDoctors": "إضافة الأطباء",
"favouriteList": "قائمة المفضلة",

@ -196,6 +196,7 @@
"enterEmail": "Please Enter Email",
"family": "My Family",
"familyTitle": "My Family Files",
"familyTitle2": "My Family Files",
"addNewMember": "Add Family Member",
"sentRequests": "Sent Requests",
"recievedRequests": "Recieved Requests",
@ -1229,6 +1230,7 @@
"hmgServices": "HMG Services",
"personalServices": "Personal Services",
"habibWallet": "Habib Wallet",
"habibWallet2": "Habib Wallet",
"loginToViewWalletBalance": "Login to view your wallet balance",
"loginToViewMedicalFile": "Login to view your medical file",
"addMember": "Add Member",

@ -94,24 +94,33 @@ class AppState {
String? get getProfileImageData => _profileImageData;
set setProfileImageData(String? value) {
// Clear old data first
if (_profileImageData != null && _profileImageData != value) {
print('🔄 Replacing profile image: old data length=${_profileImageData!.length}, new data length=${value?.length ?? 0}');
}
// Set new value directly WITHOUT caching
_profileImageData = value;
// Persist to cache
if (value != null && value.isNotEmpty) {
cacheService.saveString(key: _profileImageKey, value: value);
print('✅ Profile image set (length: ${value.length}) - NO CACHE');
} else {
cacheService.remove(key: _profileImageKey);
print('🗑️ Profile image cleared - NO CACHE');
}
}
/// Load profile image from cache on app initialization
/// Load profile image from cache on app initialization - DISABLED
void _loadProfileImageFromCache() {
_profileImageData = cacheService.getString(key: _profileImageKey);
// DO NOT LOAD FROM CACHE - always fetch fresh from API
_profileImageData = null;
print('📂 Profile image cache disabled - will fetch from API');
}
/// Clear profile image from cache (e.g., on logout)
/// Clear profile image data (e.g., on logout or user switch)
void clearProfileImageCache() {
print('🧹 Clearing profile image data (was: ${_profileImageData != null ? "${_profileImageData!.length} chars" : "null"})');
_profileImageData = null;
cacheService.remove(key: _profileImageKey);
print('✅ Profile image data cleared - NO CACHE');
}
SelectDeviceByImeiRespModelElement? _selectDeviceByImeiRespModelElement;

@ -1240,6 +1240,12 @@ class AuthenticationViewModel extends ChangeNotifier {
try {
log("Fetching profile image for patient ID: $patientID");
// IMPORTANT: Clear old cache BEFORE fetching new image
// This ensures old data is removed immediately
_appState.clearProfileImageCache();
log("🧹 Cleared profile image cache before fetch");
final profileSettingsRepo = GetIt.instance<ProfileSettingsRepo>();
final result = await profileSettingsRepo.getProfileImage(patientID: patientID);
@ -1247,6 +1253,8 @@ class AuthenticationViewModel extends ChangeNotifier {
(failure) {
// Silently fail - profile image is optional
log("Failed to fetch profile image: ${failure.message}");
// Ensure cache is cleared even on failure
_appState.setProfileImageData = null;
},
(apiResponse) {
if (apiResponse.data != null && apiResponse.data['Patient_GetProfileImageDataList'] != null) {
@ -1254,21 +1262,26 @@ class AuthenticationViewModel extends ChangeNotifier {
if (imageList is List && imageList.isNotEmpty) {
String? imageData = imageList[0]['ImageData'];
if (imageData != null && imageData.isNotEmpty) {
// Set new profile image data - this will update cache and notify all listeners
_appState.setProfileImageData = imageData;
log("✅ Profile image loaded and cached successfully");
log("✅ Profile image loaded and cached successfully for patient: $patientID");
} else {
log("⚠️ Profile image data is empty");
log("⚠️ Profile image data is empty for patient: $patientID");
_appState.setProfileImageData = null;
}
} else {
log("⚠️ Profile image list is empty");
log("⚠️ Profile image list is empty for patient: $patientID");
_appState.setProfileImageData = null;
}
} else {
log("⚠️ No profile image data in response");
log("⚠️ No profile image data in response for patient: $patientID");
_appState.setProfileImageData = null;
}
},
);
} catch (e) {
log("❌ Error fetching profile image: $e");
_appState.setProfileImageData = null;
}
}
}

@ -151,7 +151,7 @@ class ProfileSettingsRepoImp implements ProfileSettingsRepo {
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getProfileImage({
Future<Either<Failure, GenericApiModel<dynamic>>> getProfileImage({
required int patientID,
}) async {
final Map<String, dynamic> body = {

@ -189,19 +189,15 @@ class ProfileSettingsViewModel extends ChangeNotifier {
Function(String)? onError,
bool forceRefresh = false, // Add flag to force refresh
}) async {
// Skip API call if image is already loaded and not forcing refresh
if (!forceRefresh && GetIt.instance<AppState>().getProfileImageData != null) {
// Use cached data
profileImageData = GetIt.instance<AppState>().getProfileImageData;
if (onSuccess != null) {
onSuccess(profileImageData);
}
return;
}
print('🌐 Fetching profile image from API for patient: $patientID (forceRefresh: $forceRefresh)');
// IMPORTANT: Always clear old data BEFORE fetching
// Do NOT use cached data - always fetch fresh from API
profileImageData = null;
isProfileImageLoading = true;
profileImageError = null;
notifyListeners();
notifyListeners(); // Notify to show loading state
final result = await profileSettingsRepo.getProfileImage(patientID: patientID);
@ -209,6 +205,11 @@ class ProfileSettingsViewModel extends ChangeNotifier {
(failure) {
isProfileImageLoading = false;
profileImageError = failure.message;
print('❌ Failed to fetch profile image: ${failure.message}');
// Ensure data is cleared on failure
profileImageData = null;
notifyListeners();
if (onError != null) {
onError(failure.message);
@ -216,44 +217,48 @@ class ProfileSettingsViewModel extends ChangeNotifier {
},
(response) {
isProfileImageLoading = false;
// Extract image data from response
if (response.data != null && response.data['Patient_GetProfileImageDataList'] != null) {
var imageList = response.data['Patient_GetProfileImageDataList'];
if (imageList is List && imageList.isNotEmpty) {
profileImageData = imageList[0]['ImageData'];
// Store in AppState for global access
GetIt.instance<AppState>().setProfileImageData = profileImageData;
if (profileImageData != null && profileImageData!.isNotEmpty) {
// ONLY store in ViewModel - AppState will be updated separately by auth flow
print('✅ Profile image loaded from API (length: ${profileImageData!.length})');
} else {
profileImageData = null;
print('⚠️ Profile image data is empty in API response');
}
} else {
profileImageData = null;
GetIt.instance<AppState>().setProfileImageData = null;
print('⚠️ Profile image list is empty in API response');
}
} else {
profileImageData = null;
GetIt.instance<AppState>().setProfileImageData = null;
print('⚠️ No profile image data in API response');
}
notifyListeners();
notifyListeners(); // Notify to update UI with new/null data
onSuccess?.call(response.data);
},
);
}
/// Clear cached profile image data
/// Clear profile image data from ViewModel
void clearProfileImageCache() {
print('🧹 Clearing profile image from ViewModel');
profileImageData = null;
GetIt.instance<AppState>().setProfileImageData = null;
notifyListeners();
}
/// Sync profile image data from AppState
/// Sync profile image data from AppState - DISABLED
/// Profile Settings screen uses only ViewModel data from API
void syncProfileImageFromAppState() {
final appStateImageData = GetIt.instance<AppState>().getProfileImageData;
if (appStateImageData != null && appStateImageData.isNotEmpty) {
if (profileImageData != appStateImageData) {
profileImageData = appStateImageData;
notifyListeners();
}
}
// DO NOTHING - Profile Settings uses only direct API data
// No syncing from AppState to avoid stale cache issues
print('⚠️ syncProfileImageFromAppState called but disabled - use API data only');
}
Future<void> uploadProfileImage({

@ -99,6 +99,8 @@ class CodegenLoader extends AssetLoader{
"bookSuccessConfirmMore2412": "ستكون عملية الدفع عبر الإنترنت متاحة قبل 24 ساعة <20><>ن الموعد.",
"upcomingPaymentPending": "سيتم تفعيل الدفع عبر الإنترنت قبل 24 ساعة من موعد الموعد",
"upcomingPaymentNow": "ادفع عبر الإنترنت الآن لتجنب الانتظار الطويل",
"timeUntilAppointment": "الوقت المتبقي حتى الموعد",
"appointmentTimePassed": "انتهى وقت الموعد",
"upcomingQR": "استخدم رمز الاستجابة السريعة لتسجيل الوصول في المستشفى",
"upcomingVirtual": "هذا موعد افتراضي، يرجى الالتزام بالتعليمات لتجربة سلسة.",
"upcomingLivecare": "هذا موعد لايف كير",
@ -212,6 +214,7 @@ class CodegenLoader extends AssetLoader{
"enterEmail": "يرجى إدخال البريد الإلكتروني",
"family": "عائلتي",
"familyTitle": "ملفات عائلتي",
"familyTitle2": "ملفات العائلة",
"myFamily": "عائلتي",
"addNewMember": "إضافة فرد جديد للعائلة",
"sentRequests": "الطلبات المرسلة",
@ -232,7 +235,7 @@ class CodegenLoader extends AssetLoader{
"unit": "الوحدة",
"request": "طلب",
"memberName": "اسم العضو",
"switchLogin": "تبديل",
"switchLogin": "عرض",
"removeMember": "إزالة",
"allowView": "السماح",
"rejectView": "رفض",
@ -289,7 +292,7 @@ class CodegenLoader extends AssetLoader{
"parkingDescription": "خدمة المواقف هي لك لمسح موقع السيارة حتى تتمكن من العثور عليها بسهولة عند مغادرتك. انقر على زر (مسح الموقف) لحفظ موقع الموقف، ثم سيظهر زر (عرض موقفي) لإظهار الطريق إلى الموقف. إذا كنت ترغب في إعادة مسح موقف السيارة QR فقط انقر على (مسح بياناتي).",
"appointments": "المواعيد",
"all2": "الكل",
"checkinOption": "تسجيل الوصول",
"checkinOption": "تسجيل الحضور",
"readBarcode": "قراءة الكود",
"showMyPark": "عرض الموقف",
"clearMyData": "امسح البيانات",
@ -311,7 +314,7 @@ class CodegenLoader extends AssetLoader{
"radiology": "الأشعة",
"radiologySubtitle": "النتيجة",
"lab": "المختبر",
"labSubtitle": "النتائج",
"labSubtitle": "نتائج",
"medicines": "الأدوية",
"medicinesSubtitle": "الوصفات",
"vitalSigns": "علامات حيوية",
@ -327,9 +330,11 @@ class CodegenLoader extends AssetLoader{
"myMedicalSubtitle": "الأدوية",
"myDoctor": "أطبائي",
"myDoctorSubtitle": "القائمة",
"myDoctorSubtitle2": "قائمة",
"eye": "العين",
"eyeSubtitle": "القياس",
"insurance": "التأمين",
"insurance2": "تأمين",
"insuranceSubtitle": "البطاقة",
"updateInsurance": "تحديث",
"updateInsuranceSubtitle": "التأمين",
@ -344,6 +349,7 @@ class CodegenLoader extends AssetLoader{
"monthly": "شهري",
"monthlySubtitle": "التقرير",
"sick": "مريض",
"sick2": "المريضة",
"sickSubtitle": "الإجازات",
"myBalance": "رصيدي",
"myBalanceSubtitle": "الرصيد",
@ -497,7 +503,7 @@ class CodegenLoader extends AssetLoader{
"shareReview": "شارك التقييم",
"review": " التقييمات",
"viewMedicalFile": "عرض التفاصيل",
"viewAllServices": "عرض جميع الخدمات",
"viewAllServices": "جميع الخدمات",
"medicalFile": "الملف الطبي",
"verified": "تم التحقق",
"checkup": "فحص",
@ -506,9 +512,11 @@ class CodegenLoader extends AssetLoader{
"refferalTitle": "إحالة إلكترونية",
"refferalSubTitle": "الخدمة",
"healthCare": "الرعاية الصحية",
"emergency": "حالة طوارئ",
"emergency": "طوارئ",
"emergency2": "الطوارئ",
"erservices": "خدمات الطوارئ",
"services2": "الخدمات",
"services3": "خدمات",
"cantSeeProfile": "لرؤية ملفك الطبي، يرجى تسجيل الدخول أو التسجيل الآن",
"loginRegisterNow": "تسجيل الدخول أو التسجيل الآن",
"hmgPharmacy": "صيدلية الحبيب",
@ -779,6 +787,7 @@ class CodegenLoader extends AssetLoader{
"erConsultation": "تتيح لك هذه الخدمة إجراء استشارة افتراضية عبر الإنترنت عبر مكالمة فيديو مباشرة مع الطبيب من أي مكان وفي أي وقت.",
"myInvoice": "القائمة",
"invoiceList": "فواتيري",
"invoiceList2": "فواتيري",
"allInvoices": "كل الفواتير",
"hospitals": "المستشفيات",
"clinics": "العيادات",
@ -933,7 +942,7 @@ class CodegenLoader extends AssetLoader{
"totalAmountToPay": "المبلغ الإجمالي المستحق",
"vat15": "ضريبة القيمة المضافة 15%",
"liveCare": "لايف كير",
"recentVisits": "الزيارات الأخيرة",
"recentVisits": "قائمة الزيارات",
"favouriteDoctors": "الأطباء المفضلون",
"searchByClinic": "البحث حسب العيادة",
"tapToSelectClinic": "انقر لاختيار العيادة",
@ -944,7 +953,7 @@ class CodegenLoader extends AssetLoader{
"immediateConsultation": "استشارة فورية",
"scheduledConsultation": "استشارة مجدولة",
"pharmaLiveCare": "لايف كير الصيدلية",
"notSureHelpMeChooseClinic": "غير متأكد؟ ساعدني في اختيار عيادة!",
"notSureHelpMeChooseClinic": "هل تريد مساعدة في اختيار العياة؟",
"mentionYourSymptomsAndFindDoctors": "اذكر أعراضك واعثر على قائمة الأطباء وفقاً لذلك",
"immediateService": "خدمة فورية",
"noNeedToWaitGetMedicalConsultation": "لا حاجة للانتظار، ستحصل على استشارة طبية فورية عبر مكالمة فيديو",
@ -954,7 +963,7 @@ class CodegenLoader extends AssetLoader{
"freeMedicineDelivery": "توصيل مجاني للأدوية",
"offersFreeMedicineDelivery": "يوفر توصيل مجاني للأدوية لموعد لايف كير",
"dentalChiefComplaints": "الشكاوى الرئيسة للأسنان",
"viewAvailableAppointments": "عرض المواعيد المتاحة",
"viewAvailableAppointments": "المواعيد المتاحة",
"doctorProfile": " ملف الطبيب",
"waitingAppointment": "موعد الانتظار",
"hospitalInformation": "معلومات المستشفى",
@ -1008,7 +1017,7 @@ class CodegenLoader extends AssetLoader{
"processingCheckIn": "جاري معالجة تسجيل الحضور...",
"bookingWaitingAppointment": "جاري حجز موعد الانتظار، يرجى الانتظار...",
"enterValidIDorIqama": "يرجى إدخال رقم هوية وطنية أو رقم ملف صالح",
"selectAppointment": "حدد الموعد",
"selectAppointment": "اختر الموعد",
"rebookSameDoctor": "أعد الحجز مع نفس الطبيب",
"queueing": "قائمة الانتظار",
"inQueue": "في قائمة الانتظار",
@ -1181,14 +1190,22 @@ class CodegenLoader extends AssetLoader{
"monitorCholesterolLevels": "راقب مستويات الكوليسترول، وقيّم مخاطر صحة القلب، واتخذ خطوات استباقية للرفاهية.",
"triglyceridesFatBlood": "الدهون الثلاثية في الدم",
"understandTriglyceridesImpact": "افهم تأثير الدهون الثلاثية على صحة القلب مع رؤى مخصصة وتوصيات الخبراء.",
"bmiCalculator": "حاسبة\nمؤشر كتلة الجسم",
"caloriesCalculator": "حاسبة\nالسعرات الحرارية",
"bmrCalculator": "حاسبة\nمعدل الأيض الأساسي",
"idealBodyWeight": "الوزن المثالي\nللجسم",
"bodyFatCalculator": "حاسبة\nدهون الجسم",
"carbsProteinFat": "الكربوهيدرات\nالبروتين والدهون",
"ovulationPeriod": "فترة\nالإباضة",
"deliveryDueDate": "تاريخ الولادة\nالمتوقع",
"bmiCalculator": "حاسبة مؤشر كتلة الجسم",
"bmiCalculatorDesc": "احسب مؤشر كتلة جسمك لفهم نطاق وزنك الصحي.",
"caloriesCalculator": "حاسبةالسعرات الحرارية",
"caloriesCalculatorDesc": "اكتشف عدد السعرات الحرارية التي تحتاجها يومياً للحفاظ على الوزن أو فقدانه.",
"bmrCalculator": "حاسبة معدل الأيض الأساسي",
"bmrCalculatorDesc": "حدد معدل الأيض الأساسي ومتطلباتك اليومية من الطاقة.",
"idealBodyWeight": "الوزن المثالي للجسم",
"idealBodyWeightDesc": "اكتشف وزنك المثالي بناءً على الطول والعمر وبنية الجسم.",
"bodyFatCalculator": "حاسبة دهون الجسم",
"bodyFatCalculatorDesc": "قدّر نسبة دهون جسمك لتتبع أهداف اللياقة والصحة.",
"carbsProteinFat": "الكربوهيدرات البروتين والدهون",
"carbsProteinFatDesc": "وازن بين العناصر الغذائية مع النسب الصحيحة من الكربوهيدرات والبروتين والدهون.",
"ovulationPeriod": "فترة الإباضة",
"ovulationPeriodDesc": "تتبع فترة خصوبتك وخطط للحمل بدقة.",
"deliveryDueDate": "تاريخ الولادة المتوقع",
"deliveryDueDateDesc": "احسب تاريخ ولادتك المتوقع وراقب مراحل الحمل.",
"low": "منخفض",
"preDiabetic": "ما قبل السكري",
"high": "مرتفع",
@ -1233,6 +1250,7 @@ class CodegenLoader extends AssetLoader{
"hmgServices": "خدمات مجموعة الحبيب الطبية",
"personalServices": "الخدمات الشخصية",
"habibWallet": "محفظة الحبيب",
"habibWallet2": "المحفظة",
"loginToViewWalletBalance": "سجل الدخول لعرض رصيد محفظتك",
"loginToViewMedicalFile": "سجل الدخول لعرض ملفك الطبي",
"addMember": "إضافة عضو",
@ -1290,7 +1308,7 @@ class CodegenLoader extends AssetLoader{
"referenceRange": "النطاق المرجعي",
"downloadReport": "تنزيل التقرير",
"generatingReport": "جارٍ إنشاء التقرير، يرجى الانتظار...",
"noLabResults": "ليس لديك أي نتائج مختبرية حتى الآن.",
"noLabResults": "لا يوجد نتائج",
"labResultDetails": "تفاصيل نتائج المختبر",
"resultOf": "نتيجة {date}",
"whatIsThisResult": "ما هي هذه النتيجة؟",
@ -1302,7 +1320,7 @@ class CodegenLoader extends AssetLoader{
"rebook": "إعادة الحجز",
"noOphthalmologyAppointments": "لم يتم العثور على أي مواعيد في قسم طب العيون...",
"noVitalSignsRecordedYet": "لا توجد علامات حيوية مسجلة بعد",
"appointmentsAndVisits": "المواعيد والزيارات",
"appointmentsAndVisits": "المواعيد",
"labAndRadiology": "المختبر والأشعة",
"activeMedicationsAndPrescriptions": "الوصفات الطبية",
"allPrescriptions": "جميع الوصفات",
@ -1463,7 +1481,7 @@ class CodegenLoader extends AssetLoader{
"beforeSyncingDataFollowInstructions": "قبل مزامنة البيانات، يرجى التأكد من اتباع التعليمات بشكل صحيح.",
"viewWatchInstructions": "عرض تعليمات الساعة",
"healthConnectAppNotInstalled": "يبدو أنه ليس لديك تطبيق Health Connect مثبتًا. يرجى تثبيته من متجر Play لمزامنة بيانات صحتك.",
"setTimerOfReminder": "ضبط مؤقت التذكير",
"setTimerOfReminder": "ضبط التذكير",
"youHaveAppointmentWithDr": "لديك موعد مع د. ",
"hours": "ساعات",
"minutes": "دقائق",
@ -1477,6 +1495,7 @@ class CodegenLoader extends AssetLoader{
"distanceDescription": "المسافة المقطوعة على مدار اليوم",
"overview": "نظرة عامة",
"details": "التفاصيل",
"details2": "تفاصيل",
"healthy": "صحي",
"warning": "تحذير",
"vitalSignDetails": "تفاصيل العلامات الحيوية",
@ -1526,7 +1545,7 @@ class CodegenLoader extends AssetLoader{
"notifyMeBeforeConsumptionTime": "أبلغني قبل وقت الاستهلاك",
"noMedicationsToday": "لا أدوية اليوم",
"reminders": "تذكيرات",
"reminderAddedToCalendar": "تمت إضافة تذكير إلى التقويم ",
"reminderAddedToCalendar": "تمت إضافة تذكير إلى التقويم ",
"errorWhileSettingCalendar": "حدث خطأ أثناء ضبط التقويم:{error}",
"instructions": "التعليمات",
"requests": "الطلبات",
@ -1552,14 +1571,14 @@ class CodegenLoader extends AssetLoader{
"bmi": "مؤشر كتلة الجسم",
"yesPleaseINeedHelp": "نعم، من فضلك أريد المساعدة",
"aiOverView": "AI ملخص",
"generateAiAnalysis": "قم بإجراء تحليل لهذا الطلب AI",
"generateAiAnalysis": "التحليل باستخدام الذكاء الاصطناعي",
"aiAnalysis": "AI تحليل",
"prediction": "تنبؤ",
"prediction": "التوقعات",
"prevention": "وقاية",
"disclaimer": "تنصل",
"thisAboveInfo": "المعلومات المذكورة أعلاه مُولّدة بواسطة AI بناءً على نتائج تحاليلك المخبرية، وهي لأغراض إعلامية فقط. لا ينبغي اعتبارها تشخيصًا طبيًا أو خطة علاجية. استشر طبيبك دائمًا للحصول على نصيحة طبية مُخصصة قبل اتخاذ أي قرارات تتعلق بصحتك.",
"aiDisclaimer": "سيتم مشاركة بيانات نتائج تحاليلك المخبرية بشكل آمن مع خدمة الذكاء الاصطناعي لدينا لتحليلها. يساعد هذا في تقديم رؤى صحية مُخصصة. هل ترغب في المتابعة؟",
"consent": "موافقة",
"consent": "نتائج المختبر",
"bmiNormalDescription": "مؤشر كتلة جسمك ضمن المعدل الصحي. استمر في اتباع نظام غذائي متوازن، وممارسة النشاط البدني بانتظام، وإجراء الفحوصات الدورية للحفاظ على صحة جيدة. راقب أي تغييرات ملحوظة مع مرور الوقت.",
"bmiUnderweightDescription": "يشير مؤشر كتلة الجسم إلى أنك تعاني من نقص الوزن. لذا، يُنصح بزيادة السعرات الحرارية المتناولة من خلال تناول الأطعمة الغنية بالعناصر الغذائية، وممارسة تمارين المقاومة لبناء العضلات، واستشارة أخصائي رعاية صحية للتحقق من وجود أسباب كامنة.",
"bmiOverweightDescription": "يشير مؤشر كتلة الجسم إلى زيادة الوزن. لذا، يُنصح بالسعي إلى إنقاص الوزن تدريجياً وبشكل مستدام من خلال تقليل السعرات الحرارية بشكل معتدل، وممارسة النشاط البدني بانتظام، واختيار الأطعمة الصحية. استشر طبيبك قبل إجراء أي تغييرات جذرية في نمط حياتك.",
@ -1634,7 +1653,7 @@ class CodegenLoader extends AssetLoader{
"insuranceRequestSubmittedSuccessfully": "تم إرسال طلب تحديث بيانات التأمين بنجاح. سيتم إعلامك بمجرد الانتهاء.",
"updatingEmailAddress": "جارٍ تحديث عنوان البريد الإلكتروني، يرجى الانتظار...",
"verifyInsurance": "التحقق من التأمين",
"tests": "تحليل",
"tests": "تحاليل",
"calendarPermissionAlert": "يرجى منح إذن الوصول إلى التقويم من إعدادات التطبيق لضبط تذكير تناول الدواء.",
"sortByNearestLocation": "فرز حسب الأقرب إلى موقعك",
"giveLocationPermissionForNearestList": "يرجى منح إذن الوصول إلى الموقع من إعدادات التطبيق لعرض أقرب المواقع.",
@ -1661,7 +1680,7 @@ class CodegenLoader extends AssetLoader{
"homeHealthCareServiceHeader": "صحتك، في أبهى صورها. رعاية فائقة الجودة، تصلك إلى عتبة دارك.",
"homeHealthCareServiceDescription": "نقدم لكم رعاية صحية عالية الجودة تصلكم إلى عتبة منزلكم. ممرضات ذوات خبرة يقدمون رعاية حانية في راحة منزلكم.",
"profileOnlyText": "الملف الشخصي",
"information": "معلومة",
"information": "معلومات الطبيب",
"noFavouriteDoctors": "ليس لديك أي قائمة مفضلة حتى الآن",
"addDoctors": "إضافة الأطباء",
"favouriteList": "قائمة المفضلة",
@ -1800,7 +1819,7 @@ static const Map<String,dynamic> _en_US = {
"date": "Date",
"time": "Time",
"pendingPayment": "Pending for payment",
"payNow": "Pay Now",
"payNow": "Payment",
"viewQR": "View QR Code",
"instruction": "Instructions",
"livecare": "LiveCare",
@ -1927,6 +1946,7 @@ static const Map<String,dynamic> _en_US = {
"enterEmail": "Please Enter Email",
"family": "My Family",
"familyTitle": "My Family Files",
"familyTitle2": "My Family Files",
"addNewMember": "Add Family Member",
"sentRequests": "Sent Requests",
"recievedRequests": "Recieved Requests",
@ -2042,6 +2062,7 @@ static const Map<String,dynamic> _en_US = {
"myMedicalSubtitle": "Medications",
"myDoctor": "My Doctors",
"myDoctorSubtitle": "List",
"myDoctorSubtitle2": "List",
"eye": "Eye",
"eyeSubtitle": "Measurement",
"insurance": "Insurance",
@ -2059,6 +2080,7 @@ static const Map<String,dynamic> _en_US = {
"monthly": "Monthly",
"monthlySubtitle": "Report",
"sick": "Sick",
"sick2": "Sick",
"sickSubtitle": "Leaves",
"myBalance": "My Balance",
"myBalanceSubtitle": "Credit",
@ -2216,8 +2238,10 @@ static const Map<String,dynamic> _en_US = {
"refferalSubTitle": "Service",
"healthCare": "Health Care",
"emergency": "Emergency",
"emergency2": "Emergency",
"erservices": "Emergency",
"services2": "Services",
"services3": "Services",
"cantSeeProfile": "To view your medical profile, please log in or register now",
"loginRegisterNow": "Login or Register Now",
"hmgPharmacy": "HMG Pharmacy",
@ -2489,6 +2513,7 @@ static const Map<String,dynamic> _en_US = {
"erConsultation": "This service allows you to make an online virtual consultation via video call directly with the doctor from anywhere at any time.",
"myInvoice": "List",
"invoiceList": "My Invoice",
"invoiceList2": "My Invoice",
"allInvoices": "All Invoices",
"hospitals": "Hospitals",
"clinics": "Clinics",
@ -2894,13 +2919,21 @@ static const Map<String,dynamic> _en_US = {
"triglyceridesFatBlood": "Triglycerides Fat Blood",
"understandTriglyceridesImpact": "Understand triglycerides' impact on heart health with personalized insights and expert recommendations.",
"bmiCalculator": "BMI Calculator",
"bmiCalculatorDesc": "Calculate your body mass index to understand your healthy weight range.",
"caloriesCalculator": "Calories Calculator",
"caloriesCalculatorDesc": "Find out how many calories you need daily to maintain or lose weight.",
"bmrCalculator": "BMR Calculator",
"bmrCalculatorDesc": "Determine your basal metabolic rate and daily energy requirements.",
"idealBodyWeight": "Ideal Body Weight",
"idealBodyWeightDesc": "Discover your ideal weight based on height, age, and body structure.",
"bodyFatCalculator": "Body Fat Calculator",
"bodyFatCalculatorDesc": "Estimate your body fat percentage to track fitness and health goals.",
"carbsProteinFat": "Carbs Protein & Fat",
"carbsProteinFatDesc": "Balance your macros with the right carbs, protein, and fat ratios.",
"ovulationPeriod": "Ovulation Period",
"ovulationPeriodDesc": "Track your fertile window and plan for conception with accuracy.",
"deliveryDueDate": "Delivery Due Date",
"deliveryDueDateDesc": "Calculate your expected delivery date and monitor pregnancy milestones.",
"low": "Low",
"preDiabetic": "Pre-diabetic",
"high": "High",
@ -2945,6 +2978,7 @@ static const Map<String,dynamic> _en_US = {
"hmgServices": "HMG Services",
"personalServices": "Personal Services",
"habibWallet": "Habib Wallet",
"habibWallet2": "Habib Wallet",
"loginToViewWalletBalance": "Login to view your wallet balance",
"loginToViewMedicalFile": "Login to view your medical file",
"addMember": "Add Member",
@ -3189,6 +3223,7 @@ static const Map<String,dynamic> _en_US = {
"distanceDescription": "Distance covered throughout the day",
"overview": "Overview",
"details": "Details",
"details2": "Details",
"healthy": "Healthy",
"warning": "Warning",
"vitalSignDetails": "Vital Sign Details",

File diff suppressed because it is too large Load Diff

@ -200,6 +200,7 @@ abstract class LocaleKeys {
static const enterEmail = 'enterEmail';
static const family = 'family';
static const familyTitle = 'familyTitle';
static const familyTitle2 = 'familyTitle2';
static const myFamily = 'myFamily';
static const addNewMember = 'addNewMember';
static const sentRequests = 'sentRequests';
@ -1236,6 +1237,7 @@ abstract class LocaleKeys {
static const hmgServices = 'hmgServices';
static const personalServices = 'personalServices';
static const habibWallet = 'habibWallet';
static const habibWallet2 = 'habibWallet2';
static const loginToViewWalletBalance = 'loginToViewWalletBalance';
static const loginToViewMedicalFile = 'loginToViewMedicalFile';
static const addMember = 'addMember';

@ -28,6 +28,7 @@ import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'dart:ui' as ui;
import 'package:hmg_patient_app_new/presentation/appointments/appointment_payment_page.dart';
import 'package:lottie/lottie.dart';
class AppointmentCard extends StatefulWidget {
final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
@ -705,42 +706,46 @@ class _AppointmentCardState extends State<AppointmentCard> {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Message text
Lottie.asset(AppAnimations.warningAnimation,
repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 12,),
LocaleKeys.upcomingPaymentPending.tr(context: context).toText14(
color: AppColors.textColor,
isCenter: true,
),
SizedBox(height: 24.h),
// Countdown Timer - DD : HH : MM : SS format with labels
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Days
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00',
LocaleKeys.days.tr(context: context),
),
_buildTimeSeparator(),
// Hours
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00',
LocaleKeys.hours.tr(context: context),
),
_buildTimeSeparator(),
// Minutes
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.minutes.tr(context: context),
),
_buildTimeSeparator(),
// Seconds
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.seconds.tr(context: context),
),
],
),
// Row(
// mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// // Days
// _buildTimeUnit(
// _timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00',
// LocaleKeys.days.tr(context: context),
// ),
// _buildTimeSeparator(),
// // Hours
// _buildTimeUnit(
// _timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00',
// LocaleKeys.hours.tr(context: context),
// ),
// _buildTimeSeparator(),
// // Minutes
// _buildTimeUnit(
// _timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00',
// LocaleKeys.minutes.tr(context: context),
// ),
// _buildTimeSeparator(),
// // Seconds
// _buildTimeUnit(
// _timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00',
// LocaleKeys.seconds.tr(context: context),
// ),
// ],
// ),
SizedBox(height: 24.h),
// Green Acknowledge button with checkmark icon
CustomButton(

@ -26,7 +26,7 @@ class HabibWalletCard extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.myBalance.tr(context: context).toText16(isBold: true),
LocaleKeys.myBalanceSubtitle.tr(context: context).toText16(isBold: true),
// Row(
// children: [
// LocaleKeys.viewAllServices.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true),
@ -57,7 +57,7 @@ class HabibWalletCard extends StatelessWidget {
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
LocaleKeys.habibWallet.tr(context: context).toText16(isBold: true, letterSpacing: -0.2),
LocaleKeys.habibWallet2.tr(context: context).toText16(isBold: true, letterSpacing: -0.2),
// Container(
// height: 40.h,
// width: 40.h,

@ -34,13 +34,12 @@ class _ProfilePictureWidgetState extends State<ProfilePictureWidget> {
void initState() {
super.initState();
_currentPatientId = _appState.getAuthenticatedUser()?.patientId;
print('🎬 ProfilePictureWidget initState - patient: $_currentPatientId');
// Use addPostFrameCallback to ensure widget is built before loading
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
final profileVm = context.read<ProfileSettingsViewModel>();
final patientID = _appState.getAuthenticatedUser()?.patientId;
if (patientID == null) {
@ -48,20 +47,9 @@ class _ProfilePictureWidgetState extends State<ProfilePictureWidget> {
return;
}
// Check if we have data in AppState that matches current user
final appStateImageData = _appState.getProfileImageData;
if (appStateImageData != null && appStateImageData.isNotEmpty) {
// Sync to ViewModel if it doesn't have data
if (profileVm.profileImageData == null || profileVm.profileImageData!.isEmpty) {
print('🔄 Syncing AppState data to ViewModel');
profileVm.syncProfileImageFromAppState();
}
} else {
// No cached data - load from API
print('📥 No cached data - loading from API for patient: $patientID');
_loadProfileImage(forceRefresh: false);
}
// Always load fresh data from API - do NOT use AppState cache
print('📥 Loading fresh profile image from API for patient: $patientID');
_loadProfileImage(forceRefresh: true);
});
}
@ -88,13 +76,25 @@ class _ProfilePictureWidgetState extends State<ProfilePictureWidget> {
final oldPatientId = _currentPatientId;
_currentPatientId = currentPatientId;
// Clear the old profile image data
// Clear the old profile image data from BOTH AppState and ViewModel
try {
final profileVm = context.read<ProfileSettingsViewModel>();
print('🧹 Clearing cache for old user: $oldPatientId');
// Clear AppState cache first
_appState.clearProfileImageCache();
// Then clear ViewModel cache
profileVm.clearProfileImageCache();
// Force rebuild to show default avatar immediately
if (mounted) {
setState(() {
_selectedImage = null; // Clear any selected image
});
}
print('📥 Loading profile image for new user: $currentPatientId');
// Load the new user's profile image immediately
profileVm.getProfileImage(
@ -455,6 +455,7 @@ class _ProfilePictureWidgetState extends State<ProfilePictureWidget> {
Widget _buildProfileImage(ProfileSettingsViewModel profileVm) {
// Always get fresh user data
final currentUser = _appState.getAuthenticatedUser();
final currentPatientId = currentUser?.patientId;
final gender = currentUser?.gender ?? 1;
final age = currentUser?.age ?? 0;
@ -480,11 +481,22 @@ class _ProfilePictureWidgetState extends State<ProfilePictureWidget> {
);
}
// Use ViewModel data if available, otherwise fall back to AppState
// This ensures we show the current logged-in user's image (same as homepage profile icon)
final String? imageData = profileVm.profileImageData ?? _appState.getProfileImageData;
// // IMPORTANT: Verify the cached image belongs to the current user
// // If _currentPatientId doesn't match currentPatientId, don't show cached image
// if (currentPatientId != _currentPatientId) {
// print('⚠️ Patient ID mismatch - showing default avatar (current: $currentPatientId, cached: $_currentPatientId)');
// return Image.asset(
// defaultImage,
// width: 136.w,
// height: 136.h,
// );
// }
// IMPORTANT: Use ONLY ViewModel data, DO NOT fallback to AppState
// This prevents showing stale cached data when switching users
final String? imageData = profileVm.profileImageData;
print('🖼️ Building profile image - has data: ${imageData != null && imageData.isNotEmpty}, patient: ${currentUser?.patientId}');
print('🖼️ Building profile image - has data: ${imageData != null && imageData.isNotEmpty}, patient: $currentPatientId (ViewModel ONLY)');
// Show uploaded image if available
if (imageData != null && imageData.isNotEmpty) {
@ -510,7 +522,7 @@ class _ProfilePictureWidgetState extends State<ProfilePictureWidget> {
}
// Show default image (no image data or user has no uploaded image)
print('📷 Showing default avatar for user ${currentUser?.patientId}');
print('📷 Showing default avatar for user $currentPatientId');
return Image.asset(
defaultImage,
width: 136.w,

Loading…
Cancel
Save