diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index ffa3a908..f703aeb8 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -50,6 +50,8 @@
android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
tools:node="remove" />
+
+
@@ -92,7 +94,6 @@
-
diff --git a/android/app/src/main2/AndroidManifest.xml b/android/app/src/main2/AndroidManifest.xml
index e07739b9..535bd4ab 100644
--- a/android/app/src/main2/AndroidManifest.xml
+++ b/android/app/src/main2/AndroidManifest.xml
@@ -45,6 +45,7 @@
+
-
diff --git a/assets/fonts/cairo/Cairo-Bold.ttf b/assets/fonts/cairo/Cairo-Bold.ttf
new file mode 100644
index 00000000..041abaf6
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Bold.ttf differ
diff --git a/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.eot b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.eot
new file mode 100644
index 00000000..ed5f0c34
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.eot differ
diff --git a/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.otf b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.otf
new file mode 100644
index 00000000..8bc25fbb
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.otf differ
diff --git a/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.ttf b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.ttf
new file mode 100644
index 00000000..4f026897
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.ttf differ
diff --git a/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.woff b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.woff
new file mode 100644
index 00000000..91e3d063
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.woff differ
diff --git a/assets/fonts/cairo/Cairo-ExtraBold.ttf b/assets/fonts/cairo/Cairo-ExtraBold.ttf
new file mode 100644
index 00000000..4fc547de
Binary files /dev/null and b/assets/fonts/cairo/Cairo-ExtraBold.ttf differ
diff --git a/assets/fonts/cairo/Cairo-Light.ttf b/assets/fonts/cairo/Cairo-Light.ttf
new file mode 100644
index 00000000..7e0f9427
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Light.ttf differ
diff --git a/assets/fonts/cairo/Cairo-Light/Cairo-Light.eot b/assets/fonts/cairo/Cairo-Light/Cairo-Light.eot
new file mode 100644
index 00000000..d76c5391
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Light/Cairo-Light.eot differ
diff --git a/assets/fonts/cairo/Cairo-Light/Cairo-Light.otf b/assets/fonts/cairo/Cairo-Light/Cairo-Light.otf
new file mode 100644
index 00000000..f8812a9c
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Light/Cairo-Light.otf differ
diff --git a/assets/fonts/cairo/Cairo-Light/Cairo-Light.ttf b/assets/fonts/cairo/Cairo-Light/Cairo-Light.ttf
new file mode 100644
index 00000000..840cbc3e
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Light/Cairo-Light.ttf differ
diff --git a/assets/fonts/cairo/Cairo-Light/Cairo-Light.woff b/assets/fonts/cairo/Cairo-Light/Cairo-Light.woff
new file mode 100644
index 00000000..9534bb4b
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Light/Cairo-Light.woff differ
diff --git a/assets/fonts/cairo/Cairo-Medium.ttf b/assets/fonts/cairo/Cairo-Medium.ttf
new file mode 100644
index 00000000..5b8d0f70
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Medium.ttf differ
diff --git a/assets/fonts/cairo/Cairo-Regular.ttf b/assets/fonts/cairo/Cairo-Regular.ttf
new file mode 100644
index 00000000..36ac46bf
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Regular.ttf differ
diff --git a/assets/fonts/cairo/Cairo-Regular/Cairo-Regular.ttf b/assets/fonts/cairo/Cairo-Regular/Cairo-Regular.ttf
new file mode 100644
index 00000000..3eb09f86
Binary files /dev/null and b/assets/fonts/cairo/Cairo-Regular/Cairo-Regular.ttf differ
diff --git a/assets/fonts/cairo/Cairo-SemiBold.ttf b/assets/fonts/cairo/Cairo-SemiBold.ttf
new file mode 100644
index 00000000..fd01d422
Binary files /dev/null and b/assets/fonts/cairo/Cairo-SemiBold.ttf differ
diff --git a/assets/images/png/Carparking_services_image.png b/assets/images/png/Carparking_services_image.png
new file mode 100644
index 00000000..9ce08c78
Binary files /dev/null and b/assets/images/png/Carparking_services_image.png differ
diff --git a/assets/images/png/ereferral_services_image.png b/assets/images/png/ereferral_services_image.png
index 15194b59..10d0d14a 100644
Binary files a/assets/images/png/ereferral_services_image.png and b/assets/images/png/ereferral_services_image.png differ
diff --git a/assets/images/png/full_body_back_female.png b/assets/images/png/full_body_back_female.png
index 9b3aa74d..b1ea5cac 100644
Binary files a/assets/images/png/full_body_back_female.png and b/assets/images/png/full_body_back_female.png differ
diff --git a/assets/images/png/full_body_back_male.png b/assets/images/png/full_body_back_male.png
index 2eda5e8f..73a0b8b3 100644
Binary files a/assets/images/png/full_body_back_male.png and b/assets/images/png/full_body_back_male.png differ
diff --git a/assets/images/png/full_body_front_female.png b/assets/images/png/full_body_front_female.png
index c89608b0..68ce3738 100644
Binary files a/assets/images/png/full_body_front_female.png and b/assets/images/png/full_body_front_female.png differ
diff --git a/assets/images/png/full_body_front_male.png b/assets/images/png/full_body_front_male.png
index d1f34b4e..01e603a4 100644
Binary files a/assets/images/png/full_body_front_male.png and b/assets/images/png/full_body_front_male.png differ
diff --git a/assets/images/png/healthtrackers_services_image.png b/assets/images/png/healthtrackers_services_image.png
new file mode 100644
index 00000000..2d0c3f8a
Binary files /dev/null and b/assets/images/png/healthtrackers_services_image.png differ
diff --git a/assets/images/png/livechat_services_image.png b/assets/images/png/livechat_services_image.png
new file mode 100644
index 00000000..7e33a933
Binary files /dev/null and b/assets/images/png/livechat_services_image.png differ
diff --git a/assets/images/png/smartwatch_services_image.png b/assets/images/png/smartwatch_services_image.png
new file mode 100644
index 00000000..303737bf
Binary files /dev/null and b/assets/images/png/smartwatch_services_image.png differ
diff --git a/assets/images/svg/add_new_family_icon.svg b/assets/images/svg/add_new_family_icon.svg
new file mode 100644
index 00000000..ad7ee356
--- /dev/null
+++ b/assets/images/svg/add_new_family_icon.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/svg/appointment_rating_icon.svg b/assets/images/svg/appointment_rating_icon.svg
new file mode 100644
index 00000000..83d89feb
--- /dev/null
+++ b/assets/images/svg/appointment_rating_icon.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/svg/calendar_filled_icon.svg b/assets/images/svg/calendar_filled_icon.svg
new file mode 100644
index 00000000..116ce8ef
--- /dev/null
+++ b/assets/images/svg/calendar_filled_icon.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json
index fe4c04d8..92f202f0 100644
--- a/assets/langs/ar-SA.json
+++ b/assets/langs/ar-SA.json
@@ -3,6 +3,8 @@
"arabic": "العربية",
"login": "تسجيل الدخول",
"noDataAvailable": "لا توجد بيانات متاحة",
+ "noRatingAvailable": "لا يوجد تقييم متاح",
+ "doctorDoesNotHaveRating": "لم يحصل هذا الطبيب على أي تقييمات بعد.",
"confirm": "تأكيد",
"loadingText": "جاري التحميل، يرجى الانتظار...",
"kilometerUnit": "كم",
@@ -301,7 +303,7 @@
"vitalSignsSubTitle": "التقارير",
"myMedical": "نشط",
"myMedicalSubtitle": "الأدوية",
- "myDoctor": "طبيبي",
+ "myDoctor": "أطبائي",
"myDoctorSubtitle": "القائمة",
"eye": "العين",
"eyeSubtitle": "القياس",
@@ -363,7 +365,7 @@
"paymentOnline": "الخدمة",
"onlineCheckIn": "تسجيل الوصول عبر الإنترنت",
"myBalances": "رصيدي",
- "myWallet": "محف��تي",
+ "myWallet": "محفظتي",
"balanceAmount": "مبلغ المحفظة",
"totalBalance": "إجمالي الرصيد",
"createAdvancedPayment": "إعادة شحن المحفظة",
@@ -487,7 +489,7 @@
"services2": "الخدمات",
"cantSeeProfile": "لرؤية ملفك الطبي، يرجى تسجيل الدخول أو التسجيل الآن",
"loginRegisterNow": "تسجيل الدخول أو التسجيل الآن",
- "hmgPharmacy": "صيدلية مجموعة الحبيب الطبية",
+ "hmgPharmacy": "صيدلية الحبيب",
"ecommerceSolution": "حلول التجارة الإلكترونية",
"comprehensive": "شامل",
"onlineConsulting": "استشارات عبر الإنترنت",
@@ -858,7 +860,7 @@
"onboardingBody1": "ببضع نقرات فقط يمكنك استشارة الطبيب الذي تختاره.",
"onboardingHeading2": "الوصول إلى السجل الطبي بين يديك",
"onboardingBody2": "تتبع تاريخك الطبي بما في ذلك الفحوصات المخبرية، الوصفات الطبية، التأمين، وغيرها.",
- "hmgHospitals": "مستشفيات مجموعة الحبيب الطبية",
+ "hmgHospitals": "مستشفيات الحبيب",
"hmcMedicalClinic": "مراكز مجموعة الحبيب الطبية",
"applyFilter": "تطبيق الفلتر",
"facilityAndLocation": "المرفق والموقع",
@@ -934,7 +936,7 @@
"hospitalInformation": "معلومات المستشفى",
"fetchingAppointmentShare": "جاري جلب تفاصيل الموعد...",
"bookingYourAppointment": "جاري حجز موعدك...",
- "selectLiveCareClinic": "اختر عيادة لايف كير",
+ "selectLiveCareClinic": "عيادة لايف كير",
"checkingForExistingDentalPlan": "جاري التحقق من وجود خطة أسنان حالية، يرجى الانتظار...",
"dentalTreatmentPlan": "خطة علاج الأسنان",
"youHaveExistingTreatmentPlan": "لديك خطة علاج حالية: ",
@@ -1618,5 +1620,74 @@
"sortByLocation": "الترتيب حسب الموقع",
"timeForFirstReminder": "وقت التذكير الأول",
"reminderRemovalNote": "يمكنك إزالتها من التقويم الخاص بك لاحقاً عن طريق إيقاف تشغيل التذكير",
- "communicationLanguage": "لغة التواصل"
+ "communicationLanguage": "لغة التواصل",
+
+ "cmcServiceHeader": "فحص صحي شامل: تشخيص متقدم، معلومات صحية مفصلة",
+ "cmcServiceDescription": "احصل على معلومات تفصيلية عن صحتك من خلال خدمات التشخيص المتقدمة لدينا. افهم جسمك بشكل أفضل لمستقبل صحي.",
+ "eReferralServiceHeader": "نظام الإحالة الإلكترونية في مستشفى حبيب: تبسيط عملية إحالة المرضى",
+ "eReferralServiceDescription": "نُسهّل عملية نقل المرضى بسلاسة إلى مستشفى حبيب من خلال نظام الإحالة الإلكترونية الآمن لدينا. نضمن استمرارية الرعاية لكل مريض.",
+ "bloodDonationServiceHeader": "تبرع بالدم، أنقذ الأرواح. تبرعك يُحدث فرقاً.",
+ "bloodDonationServiceDescription": "تبرّع بالدم، وأنقذ الأرواح. تبرعك يبعث الأمل. انضم لحملة التبرع بالدم وكن شريان حياة للمحتاجين. كل قطرة تُحدث فرقًا!",
+ "healthTrackersServiceHeader": "تتبّع مؤشراتك الحيوية بسهولة ويسر",
+ "healthTrackersServiceDescription": "أدخل بياناتك لمراقبة معدل ضربات القلب وضغط الدم بشكل مستمر، بالإضافة إلى ملخصات دقيقة لأنشطتك اليومية. ابقَ على اطلاع وحسّن صحتك بسهولة.",
+ "waterConsumptionServiceHeader": "حافظ على رطوبتك، حافظ على صحتك. تتبع كمية الماء التي تشربها يومياً بكل سهولة.",
+ "waterConsumptionServiceDescription": "أروِ عطشك، وتابع صحتك. راقب كمية الماء التي تتناولها يومياً بكل سهولة باستخدام تطبيقنا سهل الاستخدام، مما يضمن لك الترطيب الأمثل والصحة الجيدة.",
+ "smartWatchServiceHeader": "قم بمزامنة ساعتك الذكية مع تطبيقات الصحة",
+ "smartWatchServiceDescription": "قم بتوصيل ساعتك الذكية بسلاسة بتطبيقنا الصحي لتتبع البيانات بسهولة والحصول على رؤى شخصية.",
+ "liveChatServiceHeader": "مساعدة الخبراء على مدار الساعة طوال أيام الأسبوع\n\nمساعدة",
+ "liveChatServiceDescription": "هل تحتاج إلى مساعدة؟ تتيح لك خدمة الدردشة المباشرة لدينا التواصل مع فريق دعم الخبراء للإجابة على أي أسئلة لديك حول الميزات أو الإعدادات أو استكشاف الأخطاء وإصلاحها.",
+ "emergencyServiceHeader": "تسجيل الوصول إلى قسم الطوارئ، أسرع من أي وقت مضى. اتصل بالإسعاف / فريق الاستجابة السريعة على الفور",
+ "emergencyServiceDescription": "هل تواجه حالة طبية طارئة؟ سيارات الإسعاف وفرق الاستجابة السريعة لدينا جاهزة على مدار الساعة. بالإضافة إلى ذلك، يمكنك تسجيل دخولك إلى قسم الطوارئ بسرعة لتلقي رعاية أسرع.",
+ "homeHealthCareServiceHeader": "صحتك، في أبهى صورها. رعاية فائقة الجودة، تصلك إلى عتبة دارك.",
+ "homeHealthCareServiceDescription": "نقدم لكم رعاية صحية عالية الجودة تصلكم إلى عتبة منزلكم. ممرضات ذوات خبرة يقدمون رعاية حانية في راحة منزلكم.",
+ "profileOnlyText": "الملف الشخصي",
+ "information": "معلومة",
+ "noFavouriteDoctors": "ليس لديك أي قائمة مفضلة حتى الآن",
+ "addDoctors": "إضافة الأطباء",
+ "favouriteList": "قائمة المفضلة",
+ "later": "لاحقاً",
+ "cancelAppointmentConfirmMessage": "هل أنت متأكد من رغبتك في إلغاء هذا الموعد؟",
+ "acknowledged": "معترف به",
+ "searchLabResults": "بحث نتائج المختبر",
+ "callForAssistance": "اتصل للحصول على المساعدة الفورية",
+ "oneWaySubtitle": "نقل من الموقع إلى المستشفى",
+ "twoWaySubtitle": "نقل من الموقع إلى المستشفى والعودة مرة أخرى",
+ "toHospitalSubtitle": "نقل من موقعك الحالي إلى المستشفى",
+ "fromHospitalSubtitle": "نقل من المستشفى إلى منزلك",
+ "clinicSchedule": "جدول العيادة",
+ "doctorAddedToFavourite": "تمت إضافة إلى قائمة المفضلة",
+ "doctorRemovedFromFavourite": "تمت إزالة من قائمة الأطباء المفضلين",
+ "addNewFavorite": "إضافة مفضلة جديدة",
+ "favourites": "المفضلة",
+ "pendingAncillaryOrders": "مدفوعات طلبات الاضافية",
+ "liveCarePermissions": "يتطلب لايف كير أذونات الكاميرا والميكروفون وإشعارات، يرجى السماح لها بالمتابعة.",
+ "containsImage": "يحتوي على صورة",
+ "servicesSelected": "الخدمة (الخدمات) المختارة",
+ "selectMedicalFileFromFamily": "اختر ملف طبي من عائلتك",
+ "anyFileFromHMG": "أي ملف طبي نشط من مجموعة حبيب الطبية",
+ "noDistance": "المسافة غير متوفرة",
+ "enterAmount": "يرجى إدخال المبلغ للمتابعة.",
+ "selectHospitalContinue": "يرجى اختيار المستشفى للمتابعة.",
+ "year": "سنة",
+ "notifyPrescription": "أبلغني قبل وقت الاستهلاك",
+ "doctorRatingAppointment": "طبيب\nتصنيف",
+ "ratingSubmitted": "تم إرسال التقييم",
+ "rateTheDoctor": "يرجى تقييم الطبيب",
+ "downloadInvoice": "تحميل الفاتورة",
+ "approvalDetails": "تفاصيل الموافقة",
+ "obese": "سمين",
+ "overWeight": "وزن زائد",
+ "underWeight": "وزن ناقص",
+ "nearestGate": "أقرب بوابة",
+ "RRTDDetails": "فريق الاستجابة السريعة يقوم بتقديم رعاية صحية متكاملة لجميع الحالات الطارئة و المستقرة، الخدمة تشمل جميع الأعمار بما في ذلك حالات الأطفال.",
+ "fromHospitalSubtitle": "نقل من المستشفى إلى منزلك",
+ "normalMapType": "افتراضي",
+ "satelliteMapType": "قمر صناعي",
+ "terrainMapType": "تضاريس",
+ "hybridMapType": "هجين",
+ "openCamera": "افتح الكاميرا",
+ "openGallery": "افتح المعرض",
+ "openFiles": "فتح الملفات",
+ "generateAiAnalysisRadResult": "بإنشاء تحليل الذكاء الاصطناعي لهذا التقرير",
+ "grantLocationPermission": "يرجى منح إذن الوصول إلى الموقع من إعدادات التطبيق للحصول على نتائج أفضل."
}
diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json
index 9419b5a3..384b16d5 100644
--- a/assets/langs/en-US.json
+++ b/assets/langs/en-US.json
@@ -3,6 +3,8 @@
"arabic": "Arabic",
"login": "Login",
"noDataAvailable": "No Data Available",
+ "noRatingAvailable": "No Rating Available",
+ "doctorDoesNotHaveRating": "This doctor does not have any ratings yet.",
"confirm": "Confirm",
"loadingText": "Loading, please wait...",
"kilometerUnit": "KM",
@@ -300,7 +302,7 @@
"vitalSignsSubTitle": "Reports",
"myMedical": "Active",
"myMedicalSubtitle": "Medications",
- "myDoctor": "My Doctor",
+ "myDoctor": "My Doctors",
"myDoctorSubtitle": "List",
"eye": "Eye",
"eyeSubtitle": "Measurement",
@@ -928,7 +930,7 @@
"hospitalInformation": "Hospital Information",
"fetchingAppointmentShare": "Fetching Appointment Share...",
"bookingYourAppointment": "Booking your appointment...",
- "selectLiveCareClinic": "Select LiveCare Clinic",
+ "selectLiveCareClinic": "LiveCare Clinic",
"checkingForExistingDentalPlan": "Checking for an existing dental plan, Please wait...",
"dentalTreatmentPlan": "Dental treatment plan",
"youHaveExistingTreatmentPlan": "You have an existing treatment plan: ",
@@ -1239,7 +1241,7 @@
"quickLinks": "Quick Links",
"viewMedicalFileLandingPage": "View medical file",
"immediateLiveCareRequest": "Immediate LiveCare Request",
- "yourTurnIsAfterPatients": "Your turn is after {count} patients.",
+ "yourTurnIsAfterPatients": "Your turn is after {count} patients. Please wait for your turn, The doctor will call you shortly.",
"dontHaveHHCOrders": "You don't have any Home Health Care orders yet.",
"hhcOrders": "HHC Orders",
"requestedServices": "Requested Services",
@@ -1307,7 +1309,7 @@
"rejectedYourFamilyMemberRequest": "{status} your family member request",
"notAvailable": "N/A",
"selectAProfile": "Please select a profile",
- "switchFamilyFile": "Switch from the below list of medical file",
+ "switchFamilyFile": "Switch from the below list of medical files",
"medicalFiles": "Medical Files",
"addANewFamilyMember": "Add a new family member",
"viewInvoiceDetails": "View invoice details",
@@ -1611,5 +1613,73 @@
"sortByLocation": "Sort by location",
"sortByNearestLocation": "Sort by nearest to your location",
"giveLocationPermissionForNearestList": "Please grant location permission from app settings to see the nearest locations.",
- "communicationLanguage": "Communication Language"
+ "communicationLanguage": "Communication Language",
+ "cmcServiceHeader": "Complete Health Checkup: Advanced diagnostics, Detailed Health insights",
+ "cmcServiceDescription": "Get detailed insights into your health with our advanced diagnostics. Understand your body better for a healthier future.",
+ "eReferralServiceHeader": "HMG Hospital E-Referral: Streamlined patient referrals",
+ "eReferralServiceDescription": "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.",
+ "bloodDonationServiceHeader": "Give Blood, Save Lives. Your donation makes a difference.",
+ "bloodDonationServiceDescription": "Donate blood, empower lives. Your contribution creates hope. Join our blood drive and be a lifeline for those in need. Every drop counts!",
+ "healthTrackersServiceHeader": "Track Your Vitals with Ease and effortlessly ",
+ "healthTrackersServiceDescription": "Input your metrics for continuous heart rate monitoring, blood pressure and precise daily activity summaries. Stay informed and optimize your well-being with ease.",
+ "waterConsumptionServiceHeader": "Stay Hydrated, Stay Healthy. Track your daily water intake with ease.",
+ "waterConsumptionServiceDescription": "Quench your thirst, track your health. Effortlessly monitor your daily water intake with our user-friendly app, ensuring optimal hydration and well-being.",
+ "smartWatchServiceHeader": "Sync Your Smartwatch with Health Apps",
+ "smartWatchServiceDescription": "Seamlessly connect your smartwatch to our health app for effortless data tracking and personalized insights.",
+ "liveChatServiceHeader": "24/7 Expert\nAssistance",
+ "liveChatServiceDescription": "Need help ? Our live chat connects you with expert support for any questions about features, settings, or troubleshooting.",
+ "emergencyServiceHeader": "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly",
+ "emergencyServiceDescription": "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.",
+ "homeHealthCareServiceHeader": "Your Health, Elevated. Premium care, delivered to your doorstep.",
+ "homeHealthCareServiceDescription": "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.",
+ "profileOnlyText": "Profile",
+ "information": "Information",
+ "noFavouriteDoctors": "You don't have any favourite list yet",
+ "addDoctors": "Add Doctors",
+ "favouriteList": "Favourite List",
+ "later": "Later",
+ "cancelAppointmentConfirmMessage": "Are you sure you want to cancel this appointment?",
+ "acknowledged": "Acknowledged",
+ "searchLabResults": "Search lab results",
+ "callForAssistance": "Call for immediate assistance",
+ "oneWaySubtitle": "Pickup from location to hospital",
+ "twoWaySubtitle": "Round trip from location to hospital and back",
+ "toHospitalSubtitle": "Transfer from your current location to the hospital",
+ "fromHospitalSubtitle": "Transfer from the hospital back to your home",
+ "clinicSchedule": "Clinic Schedule",
+ "doctorAddedToFavourite": "Added to favorites list",
+ "doctorRemovedFromFavourite": "Removed from favorites list",
+ "favourites": "Favourites",
+ "addNewFavorite": "Add new favourite",
+ "pendingAncillaryOrders": "Pending ancillary orders",
+ "liveCarePermissions": "LiveCare requires Camera, Microphone & Notifications permissions to enable virtual consultation between patient & doctor, Open app settings to allow them.",
+ "containsImage": "Contains Image",
+ "servicesSelected": "Service(s) Selected",
+ "selectMedicalFileFromFamily": "Select a medical file from your family",
+ "anyFileFromHMG": "Any active medical file from HMG",
+ "noDistance": "Distance not available",
+ "enterAmount": "Please enter amount to continue.",
+ "selectHospitalContinue": "Please select hospital to continue.",
+ "year": "Year",
+ "notifyPrescription": "Notify me before the consumption time",
+ "doctorRatingAppointment": "Doctor\nRating",
+ "ratingSubmitted": "Rating Submitted",
+ "rateTheDoctor": "Please rate the doctor",
+ "downloadInvoice": "Download Invoice",
+ "approvalDetails": "Approval Details",
+ "obese": "Obese",
+ "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.",
+ "fromHospitalSubtitle": "Transfer from the hospital back to your home",
+ "normalMapType": "Default",
+ "satelliteMapType": "Satellite",
+ "terrainMapType": "Terrain",
+ "hybridMapType": "Hybrid",
+ "openCamera": "Open Camera",
+ "openGallery": "Open Gallery",
+ "openFiles": "Open Files",
+ "generateAiAnalysisRadResult": "Generate AI analysis for this result",
+ "grantLocationPermission": "Please grant location permission from app settings to see better results."
}
diff --git a/ios/Runner/ring_30Sec.mp3 b/ios/Runner/ring_30Sec.mp3
new file mode 100644
index 00000000..b5bb4b88
Binary files /dev/null and b/ios/Runner/ring_30Sec.mp3 differ
diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart
index 2de4ce5c..374ac9d1 100644
--- a/lib/core/api/api_client.dart
+++ b/lib/core/api/api_client.dart
@@ -200,7 +200,7 @@ class ApiClientImp implements ApiClient {
}
// body['TokenID'] = "@dm!n";
- // body['PatientID'] = 1231755;
+ // body['PatientID'] = 1018977;
// body['PatientTypeID'] = 1;
// body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q";
diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart
index 73cb287f..b0df11f2 100644
--- a/lib/core/api_consts.dart
+++ b/lib/core/api_consts.dart
@@ -4,7 +4,7 @@ import 'package:hmg_patient_app_new/core/enums.dart';
class ApiConsts {
static const maxSmallScreen = 660;
- static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.preProd;
+ static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@@ -233,6 +233,11 @@ class ApiConsts {
static String getAiOverViewLabOrders = "Services/Patients.svc/REST/HMGAI_Lab_Analyze_Orders_API";
static String getAiOverViewLabOrder = "Services/Patients.svc/REST/HMGAI_Lab_Analyzer_API";
+ static String googleCloudStorageARTranslationFileURL = "https://storage.googleapis.com/hmg-patientapp-translations/ar-SA.json";
+ static String googleCloudStorageENTranslationFileURL = "https://storage.googleapis.com/hmg-patientapp-translations/en-US.json";
+
+ static String googleCloudStorageENTranslationFileBaseURL = "https://storage.googleapis.com/hmg-patientapp-translations";
+
// ************ static values for Api ****************
static final double appVersionID = 20.9;
@@ -450,6 +455,7 @@ var IS_LAST_APPOITMENT_RATED = 'Services/Doctors.svc/REST/IsLastAppoitmentRated_
var GET_APPOINTMENT_DETAILS_BY_NO = 'Services/MobileNotifications.svc/REST/GetAppointmentDetailsByApptNo';
var NEW_RATE_APPOINTMENT_URL = "Services/Doctors.svc/REST/AppointmentsRating_InsertAppointmentRate";
var NEW_RATE_DOCTOR_URL = "Services/Doctors.svc/REST/DoctorsRating_InsertDoctorRate";
+var GET_DOCTORS_RATING_CHECK = "Services/Doctors.svc/REST/GetDoctorsRatingCheck";
//var GET_QR_PARKING = 'Services/SWP.svc/REST/GetQRParkingByID';
@@ -934,6 +940,8 @@ const PROFILE_SETTING = 'Services/Patients.svc/REST/GetPateintInfoForUpdate';
const SAVE_SETTING = 'Services/Patients.svc/REST/UpdatePateintInfo';
+const DOWNLOAD_INVOICE_PDF = 'Services/Notifications.svc/REST/DownloadInvoiceReport';
+
class ApiKeyConstants {
static final String googleMapsApiKey = 'AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng';
}
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index c4c3c366..e4308bd5 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -174,7 +174,7 @@ class AppAssets {
static const String warning = '$svgBasePath/warning.svg';
static const String share_location = '$svgBasePath/share_location.svg';
static const String to_arrow = '$svgBasePath/to_arrow.svg';
- static const String dual_arrow = '$svgBasePath/to_arrow.svg';
+ static const String dual_arrow = '$svgBasePath/dual_arrow.svg';
static const String forward_arrow_medium = '$svgBasePath/forward_arrow_medium.svg';
static const String eReferral = '$svgBasePath/e-referral.svg';
static const String comprehensiveCheckup = '$svgBasePath/comprehensive_checkup.svg';
@@ -220,6 +220,7 @@ class AppAssets {
static const String ibw = '$svgBasePath/ibw.svg';
static const String general_health = '$svgBasePath/general_health.svg';
static const String women_health = '$svgBasePath/women_health.svg';
+ static const String add_new_family_icon = '$svgBasePath/add_new_family_icon.svg';
static const String height = '$svgBasePath/height.svg';
static const String weight = '$svgBasePath/weight.svg';
@@ -236,6 +237,7 @@ class AppAssets {
static const String forward_top_nav_icon = '$svgBasePath/forward_top_nav_icon.svg';
static const String back_top_nav_icon = '$svgBasePath/back_top_nav_icon.svg';
static const String bluetooth = '$svgBasePath/bluetooth.svg';
+ static const String calendar_filled_icon = '$svgBasePath/calendar_filled_icon.svg';
//smartwatch
static const String watchActivity = '$svgBasePath/watch_activity.svg';
@@ -354,6 +356,7 @@ class AppAssets {
static const String darkModeIcon = '$svgBasePath/dark_mode_icon.svg';
static const String biometricLockIcon = '$svgBasePath/biometric_lock_icon.svg';
static const String imageIcon = '$svgBasePath/image_icon.svg';
+ static const String appointmentRatingIcon = '$svgBasePath/appointment_rating_icon.svg';
// PNGS //
static const String hmgLogo = '$pngBasePath/hmg_logo.png';
@@ -366,6 +369,10 @@ class AppAssets {
static const String emergencyService = '$pngBasePath/emergency_services_image.png';
static const String cmcService = '$pngBasePath/cmc_services_image.png';
static const String eReferralService = '$pngBasePath/ereferral_services_image.png';
+ static const String carParkingService = '$pngBasePath/Carparking_services_image.png';
+ static const String smartWatchService = '$pngBasePath/smartwatch_services_image.png';
+ static const String healthTrackersService = '$pngBasePath/healthtrackers_services_image.png';
+ static const String livechatService = '$pngBasePath/livechat_services_image.png';
static const String maleImg = '$pngBasePath/male_img.png';
static const String femaleImg = '$pngBasePath/female_img.png';
diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart
index 7d3ab1a4..a5a8d72a 100644
--- a/lib/core/app_state.dart
+++ b/lib/core/app_state.dart
@@ -175,4 +175,78 @@ class AppState {
setRatedVisible(bool value) {
isRatedVisible = value;
}
+
+ Map _userSwitchPayloads = {};
+ int _childSwitchCounter = 0;
+
+ Map get getUserSwitchPayloads => _userSwitchPayloads;
+
+ void addUserSwitchPayload(dynamic payload, {bool isSuperUser = false}) {
+ var patientID = payload is Map ? payload['patientid'] : null;
+
+ if (patientID == null) {
+ if (isSuperUser) {
+ _userSwitchPayloads['superUser'] = payload;
+ _childSwitchCounter = 0;
+ } else {
+ _childSwitchCounter++;
+ if (_childSwitchCounter == 1) {
+ _userSwitchPayloads['child'] = payload;
+ } else {
+ _userSwitchPayloads['child$_childSwitchCounter'] = payload;
+ }
+ }
+ return;
+ }
+
+ String? existingKey;
+ for (var entry in _userSwitchPayloads.entries) {
+ if (entry.value is Map && entry.value['PatientID'] == patientID) {
+ existingKey = entry.key;
+ break;
+ }
+ }
+
+ if (isSuperUser) {
+ if (existingKey != null && existingKey != 'superUser') {
+ _userSwitchPayloads.remove(existingKey);
+ }
+ _userSwitchPayloads['superUser'] = payload;
+ _childSwitchCounter = 0;
+ } else {
+ if (existingKey != null) {
+ _userSwitchPayloads[existingKey] = payload;
+ } else {
+ _childSwitchCounter++;
+ if (_childSwitchCounter == 1) {
+ _userSwitchPayloads['child'] = payload;
+ } else {
+ _userSwitchPayloads['child$_childSwitchCounter'] = payload;
+ }
+ }
+ }
+ }
+
+
+ dynamic getUserSwitchPayload({required String userType}) {
+ return _userSwitchPayloads[userType];
+ }
+
+ dynamic getUserSwitchPayloadByIndex({bool isSuperUser = false, int? childIndex}) {
+ if (isSuperUser) {
+ return _userSwitchPayloads['superUser'];
+ } else if (childIndex != null) {
+ if (childIndex == 1) {
+ return _userSwitchPayloads['child'];
+ } else {
+ return _userSwitchPayloads['child$childIndex'];
+ }
+ }
+ return null;
+ }
+
+ void clearUserSwitchPayloads() {
+ _userSwitchPayloads.clear();
+ _childSwitchCounter = 0;
+ }
}
diff --git a/lib/core/location_util.dart b/lib/core/location_util.dart
index 95b154ba..c56c63d4 100644
--- a/lib/core/location_util.dart
+++ b/lib/core/location_util.dart
@@ -99,7 +99,7 @@ class LocationUtils {
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
- loadingText: "Please grant location permission from app settings to see better results",
+ loadingText: LocaleKeys.grantLocationPermission.tr(),
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
@@ -124,7 +124,7 @@ class LocationUtils {
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
- loadingText: "Please grant location permission from app settings to see better results",
+ loadingText: LocaleKeys.grantLocationPermission.tr(),
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
@@ -285,7 +285,7 @@ class LocationUtils {
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
- loadingText: "Please grant location permission from app settings to see better results",
+ loadingText: LocaleKeys.grantLocationPermission.tr(),
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
diff --git a/lib/core/utils/loading_utils.dart b/lib/core/utils/loading_utils.dart
index bd24d30c..48d4f2dd 100644
--- a/lib/core/utils/loading_utils.dart
+++ b/lib/core/utils/loading_utils.dart
@@ -15,37 +15,37 @@ class LoadingUtils {
static bool get isLoading => _isLoadingVisible;
- static showFullScreenLoader({bool barrierDismissible = true, isSuccessDialog = false, String loadingText = "Loading, Please wait..."}) {
- if (!_isLoadingVisible) {
- _isLoadingVisible = true;
- final context = _navigationService.navigatorKey.currentContext;
- log("got the context in showFullScreenLoading");
- if (context == null) return;
-
- showDialog(
- barrierDismissible: barrierDismissible,
- context: context,
- barrierColor: AppColors.blackColor.withOpacity(0.5),
- useRootNavigator: false,
- useSafeArea: false,
- builder: (BuildContext context) {
- return Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 20.h,
- hasShadow: false,
- ),
- child: Material(
- child: Center(
- child: isSuccessDialog ? Utils.getSuccessWidget(loadingText: loadingText) : Utils.getLoadingWidget(loadingText: loadingText),
- ).paddingSymmetrical(24.w, 0),
- ),
- );
- }).then((value) {
- _isLoadingVisible = false;
- });
- }
- }
+ // static showFullScreenLoader({bool barrierDismissible = true, isSuccessDialog = false, String loadingText = "Loading, Please wait..."}) {
+ // if (!_isLoadingVisible) {
+ // _isLoadingVisible = true;
+ // final context = _navigationService.navigatorKey.currentContext;
+ // log("got the context in showFullScreenLoading");
+ // if (context == null) return;
+ //
+ // showDialog(
+ // barrierDismissible: barrierDismissible,
+ // context: context,
+ // barrierColor: AppColors.blackColor.withOpacity(0.5),
+ // useRootNavigator: false,
+ // useSafeArea: false,
+ // builder: (BuildContext context) {
+ // return Container(
+ // decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ // color: AppColors.whiteColor,
+ // borderRadius: 20.h,
+ // hasShadow: false,
+ // ),
+ // child: Material(
+ // child: Center(
+ // child: isSuccessDialog ? Utils.getSuccessWidget(loadingText: loadingText) : Utils.getLoadingWidget(loadingText: loadingText),
+ // ).paddingSymmetrical(24.w, 0),
+ // ),
+ // );
+ // }).then((value) {
+ // _isLoadingVisible = false;
+ // });
+ // }
+ // }
static hideFullScreenLoader() {
if (!_isLoadingVisible) return;
diff --git a/lib/core/utils/push_notification_handler.dart b/lib/core/utils/push_notification_handler.dart
index d438d449..e2925c29 100644
--- a/lib/core/utils/push_notification_handler.dart
+++ b/lib/core/utils/push_notification_handler.dart
@@ -396,10 +396,10 @@ class PushNotificationHandler {
final deviceInfo = DeviceInfoPlugin();
final androidInfo = await deviceInfo.androidInfo;
- int sdkInt = androidInfo.version.sdkInt ?? 0;
- if (sdkInt >= 33) {
- await FlutterCallkitIncoming.requestFullIntentPermission();
- }
+ // int sdkInt = androidInfo.version.sdkInt ?? 0;
+ // if (sdkInt >= 33) {
+ // await FlutterCallkitIncoming.requestFullIntentPermission();
+ // }
}
}
diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart
index f63b160b..feca055a 100644
--- a/lib/core/utils/utils.dart
+++ b/lib/core/utils/utils.dart
@@ -152,9 +152,11 @@ class Utils {
static String getDayMonthYearDateFormatted(DateTime? dateTime) {
if (dateTime == null) return "";
- return appState.isArabic()
- ? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}"
- : "${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}";
+ return
+ // appState.isArabic()
+ // ? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}"
+ // :
+ "${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}";
}
/// get month by
@@ -353,7 +355,7 @@ class Utils {
repeat: false, reverse: false, frameRate: FrameRate(60), width: width.w, height: height.h, fit: BoxFit.fill),
SizedBox(height: 16.h),
(noDataText ?? LocaleKeys.noDataAvailable.tr())
- .toText14(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true)
+ .toText14(isBold: true, color: AppColors.greyTextColor, isCenter: true)
.paddingSymmetrical(64.w, 0.h),
SizedBox(height: 16.h),
callToActionButton
@@ -369,7 +371,7 @@ class Utils {
Lottie.asset(AppAnimations.loadingAnimation,
repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
- (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true),
+ (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true, isBold: true),
SizedBox(height: 8.h),
],
).center;
@@ -382,7 +384,7 @@ class Utils {
children: [
Lottie.asset(AppAnimations.checkmark, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
- (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor).paddingSymmetrical(24.h, 0.h),
+ (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 8.h),
],
).center;
@@ -768,6 +770,7 @@ class Utils {
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ spacing: 5.w,
children: [
Image.asset(AppAssets.mada, width: 35.h, height: 35.h),
Image.asset(
diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart
index 3ad0ad92..5db5b289 100644
--- a/lib/extensions/string_extensions.dart
+++ b/lib/extensions/string_extensions.dart
@@ -24,7 +24,7 @@ extension CapExtension on String {
extension EmailValidator on String {
Widget get toWidget => Text(this);
- Widget toText8({Color? color, FontWeight? fontWeight, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text(
+ Widget toText8({bool isEnglishOnly = false, Color? color, FontWeight? fontWeight, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text(
this,
maxLines: maxlines,
overflow: textOverflow,
@@ -34,6 +34,7 @@ extension EmailValidator on String {
fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor,
letterSpacing: 0,
+ fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins',
),
);
@@ -60,7 +61,7 @@ extension EmailValidator on String {
color: color ?? AppColors.blackColor,
letterSpacing: letterSpacing,
decoration: isUnderLine ? TextDecoration.underline : null,
- fontFamily: isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins',
+ fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins',
decorationColor: color ?? AppColors.blackColor),
);
@@ -73,13 +74,14 @@ extension EmailValidator on String {
int? maxlines,
FontStyle? fontStyle,
TextOverflow? textOverflow,
- double letterSpacing = 0}) =>
+ double letterSpacing = 0, bool isEnglishOnly = false}) =>
Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: maxlines,
overflow: textOverflow,
style: TextStyle(
+ fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins',
fontSize: 9.f,
fontStyle: fontStyle ?? FontStyle.normal,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
@@ -89,7 +91,7 @@ extension EmailValidator on String {
decorationColor: color ?? AppColors.blackColor),
);
- Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0}) => Text(
+ Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0, bool isEnglishOnly = false,}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: (maxLine > 0) ? maxLine : null,
@@ -100,6 +102,7 @@ extension EmailValidator on String {
color: color ?? AppColors.blackColor,
letterSpacing: letterSpacing,
decoration: isUnderLine ? TextDecoration.underline : null,
+ fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins',
),
);
@@ -125,7 +128,7 @@ extension EmailValidator on String {
height: height,
decorationColor: isUnderLine ? AppColors.blackColor : null,
decoration: isUnderLine ? TextDecoration.underline : null,
- fontFamily: isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins',
+ fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins',
),
);
@@ -208,7 +211,7 @@ extension EmailValidator on String {
height: height,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
decoration: isUnderLine ? TextDecoration.underline : null,
- fontFamily: fontFamily ?? (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),
+ fontFamily: fontFamily ?? (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'),
decorationColor: color ?? AppColors.blackColor),
);
@@ -249,14 +252,14 @@ extension EmailValidator on String {
overflow: textOverflow,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
decoration: isUnderLine ? TextDecoration.underline : null,
- fontFamily: fontFamily ?? (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),
+ fontFamily: fontFamily ?? (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'),
decorationColor: decorationColor ?? AppColors.errorColor),
);
Widget toText17({bool isEnglishOnly = false, Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
- style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 17.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),
+ style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 17.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'),
);
Widget toText18({Color? color, FontWeight? weight, bool isBold = false, bool isCenter = false, int? maxlines, TextOverflow? textOverflow, bool isEnglishOnly = false,}) => Text(
@@ -264,7 +267,7 @@ extension EmailValidator on String {
textAlign: isCenter ? TextAlign.center : null,
this,
overflow: textOverflow,
- style: TextStyle(fontSize: 18.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4, fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),),
+ style: TextStyle(fontSize: 18.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4, fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'),),
);
Widget toText19({Color? color, bool isBold = false}) => Text(
@@ -294,36 +297,38 @@ extension EmailValidator on String {
style: TextStyle(height: 1, color: color ?? AppColors.blackColor, fontSize: 22.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
- Widget toText24({Color? color, bool isBold = false, bool isCenter = false, FontWeight? fontWeight, double? letterSpacing}) => Text(
+ Widget toText24({Color? color, bool isBold = false, bool isCenter = false, FontWeight? fontWeight, double? letterSpacing, bool isEnglishOnly = false,}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
+ fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'),
height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.f, letterSpacing: letterSpacing ?? -1, fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.normal),
);
- Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing}) => Text(
+ Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing, bool isEnglishOnly = false,}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
+ fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'),
height: height ?? 23 / 26, color: color ?? AppColors.blackColor, fontSize: 26.f, letterSpacing: letterSpacing ?? -1, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
);
Widget toText28({bool isEnglishOnly = false, Color? color, bool isBold = false, double? height, bool isCenter = false, double? letterSpacing}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
- style: TextStyle(height: height ?? 23 / 28, color: color ?? AppColors.blackColor, fontSize: 28.f, letterSpacing: letterSpacing ?? -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),
+ style: TextStyle(height: height ?? 23 / 28, color: color ?? AppColors.blackColor, fontSize: 28.f, letterSpacing: letterSpacing ?? -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'),
);
Widget toText32({bool isEnglishOnly = false, FontWeight? weight, Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
- height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.f, letterSpacing: -1, fontFamily: isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins', fontWeight: isBold ? FontWeight.bold : weight ?? FontWeight.normal),
+ height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.f, letterSpacing: -1, fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins', fontWeight: isBold ? FontWeight.bold : weight ?? FontWeight.normal),
);
Widget toText44({Color? color, bool isBold = false, bool isEnglishOnly = false,}) => Text(
this,
- style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),),
+ style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'),),
);
Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) {
@@ -429,7 +434,7 @@ extension DynamicTextStyleExtension on BuildContext {
String? fontFamily,
bool isLanguageSwitcher = false}) {
AppState appState = getIt.get();
- final family = appState.getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins';
+ final family = appState.getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins';
return TextStyle(
fontFamily: fontFamily ?? family,
fontSize: fontSize,
@@ -460,7 +465,7 @@ class FontUtils {
/// Get the appropriate font family for a specific language
static String getFontFamilyForLanguage(bool isArabic) {
- return isArabic ? 'GESSTwo' : 'Poppins';
+ return isArabic ? 'CairoArabic' : 'Poppins';
}
}
diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart
index cd8ad70d..41664758 100644
--- a/lib/features/authentication/authentication_repo.dart
+++ b/lib/features/authentication/authentication_repo.dart
@@ -6,6 +6,7 @@ import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
@@ -214,56 +215,6 @@ class AuthenticationRepoImp implements AuthenticationRepo {
int? patientID,
int? loginType}) async {
AppState appState = getIt.get();
- // if (isRegister) {
- // newRequest["activationCode"] = activationCode ?? "0000";
- // newRequest["isSilentLogin"] = activationCode != null ? false : true;
- // } else {
- // newRequest.activationCode = activationCode ?? "0000";
- // newRequest.isSilentLogin = activationCode != null ? false : true;
- // newRequest.projectOutSA = newRequest.zipCode == '966' ? false : true;
- // newRequest.isDentalAllowedBackend = false;
- // newRequest.forRegisteration = newRequest.isRegister ?? false;
- // newRequest.isRegister = false;
- // }
- // Map familyRequest = {};
- // if (isFormFamilyFile) {
- // AppState appState = getIt.get();
- // familyRequest = {};
- // familyRequest['PatientShareRequestID'] = patientShareRequestID;
- // familyRequest['ResponseID'] = responseID;
- // familyRequest['Status'] = 3;
- // familyRequest["PatientID"] = appState.getAuthenticatedUser()!.patientId ?? 0;
- // familyRequest["LogInTokenID"] = appState.getFamilyFileTokenID;
- // familyRequest["activationCode"] = activationCode ?? "0000";
- // familyRequest["PatientMobileNumber"] = newRequest.patientMobileNumber;
- // familyRequest["PatientIdentificationID"] = newRequest.patientIdentificationID;
- // }
- // Map switchRequest = {};
- // if (isSwitchUser) {
- // switchRequest = newRequest.toJson();
- //
- // switchRequest['PatientID'] = responseID;
- // switchRequest['IsSilentLogin'] = true;
- // switchRequest['LogInTokenID'] = null;
- // switchRequest['SearchType'] = 2;
- // if (loginType != 0) {
- // switchRequest['SuperUser'] = patientID;
- // switchRequest['DeviceToken'] = null;
- // } else {
- // switchRequest["LoginType"] = 2;
- // }
- //
- // if (appState.getSuperUserID == responseID) {
- // // switchRequest['LoginType'] = 3;
- // switchRequest['PatientIdentificationID'] = "";
- // // switchRequest['ProjectOutSA'] = newRequest.zipCode == '966' ? false : true;
- // switchRequest.remove('NationalID');
- // switchRequest.remove('isDentalAllowedBackend');
- // switchRequest.remove('ProjectOutSA');
- // switchRequest.remove('ForRegisteration');
- // appState.setSuperUserID = null;
- // }
- // }
if (isRegister) {
newRequest["activationCode"] = activationCode ?? "0000";
@@ -321,7 +272,6 @@ class AuthenticationRepoImp implements AuthenticationRepo {
}
}
- // Determine which API to use based on zipCode
final zipCode = _getZipCodeFromRequest(newRequest);
final isOthersCountry = zipCode == '0';
final endpoint = isFormFamilyFile
@@ -336,15 +286,40 @@ class AuthenticationRepoImp implements AuthenticationRepo {
GenericApiModel? apiResponse;
Failure? failure;
+ // Determine the payload to send
+ var requestBody = isFormFamilyFile
+ ? familyRequest
+ : isRegister
+ ? newRequest
+ : isSwitchUser
+ ? switchRequest
+ : newRequest.toJson();
+
+ if (isSwitchUser) {
+ Map allUsersPayload;
+ var switchingPatientID;
+ var superUserPatientID;
+ bool isSwitchingToSuperUser = false;
+
+ allUsersPayload = appState.getUserSwitchPayloads;
+ switchingPatientID = switchRequest["PatientID"];
+ superUserPatientID = allUsersPayload['superUser']['PatientID'];
+ isSwitchingToSuperUser = (appState.getSuperUserID == responseID) || (allUsersPayload.containsKey('superUser') && superUserPatientID == switchingPatientID);
+
+ if (isSwitchingToSuperUser) {
+ var superUserPayload = appState.getUserSwitchPayloadByIndex(isSuperUser: true);
+ if (superUserPayload != null) {
+ requestBody['DeviceToken'] = newRequest.deviceToken;
+ requestBody['ProjectOutSA'] = 0;
+ requestBody['PatientIdentificationID'] = "";
+ requestBody.removeWhere((key, value) => ['NationalID', 'isDentalAllowedBackend', 'ForRegisteration'].contains(key));
+ }
+ }
+ }
+
await apiClient.post(
endpoint,
- body: isFormFamilyFile
- ? familyRequest
- : isRegister
- ? newRequest
- : isSwitchUser
- ? switchRequest
- : newRequest.toJson(),
+ body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
@@ -611,7 +586,7 @@ class AuthenticationRepoImp implements AuthenticationRepo {
}
@override
- Future>> getServicePrivilege() {
+ Future> getServicePrivilege() {
Map mapDevice = {};
try {
GenericApiModel? apiResponse;
@@ -692,6 +667,7 @@ class AuthenticationRepoImp implements AuthenticationRepo {
@override
Future>> getPatientBloodType() async {
+ // Map requestBody = {"PatientType": 1, "PatientTypeID": 1, "TokenID": "@dm!n", "PatientID": 5957754};
Map requestBody = {};
try {
GenericApiModel? apiResponse;
diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart
index c0bc4dd1..2055deea 100644
--- a/lib/features/authentication/authentication_view_model.dart
+++ b/lib/features/authentication/authentication_view_model.dart
@@ -31,6 +31,7 @@ import 'package:hmg_patient_app_new/features/authentication/models/resp_models/s
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/authentication/saved_login_screen.dart';
@@ -100,7 +101,10 @@ class AuthenticationViewModel extends ChangeNotifier {
var healthId;
int getDeviceLastLogin = 1;
+ bool isInitialLoginNavigated = false;
+
Future onLoginPressed() async {
+ setIsInitialLoginNavigated(false);
try {
LoaderBottomSheet.showLoader();
//TODO: We will remove this delay
@@ -110,7 +114,7 @@ class AuthenticationViewModel extends ChangeNotifier {
if (data != null) {
await _handleExistingImeiData(data);
} else {
- await _handleNewImeiRegistration();
+ await handleNewImeiRegistration();
}
} catch (e) {
log("Error in onLoginPressed: $e");
@@ -131,6 +135,8 @@ class AuthenticationViewModel extends ChangeNotifier {
pickedCountryByUAEUser = null;
_appState.setUserRegistrationPayload = RegistrationDataModelPayload();
_appState.setNHICUserData = CheckUserStatusResponseNHIC();
+ getIt.get().setSelectedHeight(0);
+ getIt.get().setSelectedWeight(0);
}
void onCountryChange(CountryEnum country) {
@@ -196,6 +202,11 @@ class AuthenticationViewModel extends ChangeNotifier {
}
}
+ setIsInitialLoginNavigated(bool isInitialLoginNavigated) {
+ this.isInitialLoginNavigated = isInitialLoginNavigated;
+ notifyListeners();
+ }
+
void onTermAccepted() {
isTermsAccepted = !isTermsAccepted;
notifyListeners();
@@ -213,7 +224,7 @@ class AuthenticationViewModel extends ChangeNotifier {
_appState.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken);
}
- Future selectDeviceImei({required Function(dynamic data) onSuccess, Function(String)? onError}) async {
+ Future selectDeviceImei({bool isNavigate = true, required Function(dynamic data) onSuccess, Function(String)? onError}) async {
// LoadingUtils.showFullScreenLoading();
// String firebaseToken = _appState.deviceToken;
String firebaseToken = await Utils.getStringFromPrefs(CacheConst.pushToken);
@@ -228,7 +239,9 @@ class AuthenticationViewModel extends ChangeNotifier {
// LoadingUtils.hideFullScreenLoader();
// await _errorHandlerService.handleError(failure: failure);
LoaderBottomSheet.hideLoader();
- _navigationService.pushPage(page: LoginScreen());
+ if (isNavigate) {
+ _navigationService.pushPage(page: LoginScreen());
+ }
},
(apiResponse) {
// LoadingUtils.hideFullScreenLoader();
@@ -260,10 +273,13 @@ class AuthenticationViewModel extends ChangeNotifier {
}
}
- Future _handleNewImeiRegistration() async {
- await selectDeviceImei(onSuccess: (dynamic respData) async {
- try {
- if (respData != null) {
+ Future handleNewImeiRegistration({bool isNavigate = true}) async {
+ print("handleNewImeiRegistration called");
+ await selectDeviceImei(
+ isNavigate: isNavigate,
+ onSuccess: (dynamic respData) async {
+ try {
+ if (respData != null) {
dynamic data = await SelectDeviceByImeiRespModelElement.fromJson(respData.toJson());
_appState.setSelectDeviceByImeiRespModelElement(data);
LoaderBottomSheet.hideLoader();
@@ -271,19 +287,24 @@ class AuthenticationViewModel extends ChangeNotifier {
// TODO: Navigate to SavedLogin when available
// SelectDeviceByImeiRespModelElement savedData =
// SelectDeviceByImeiRespModelElement.fromJson(respData);
- _navigationService.pushPage(page: SavedLogin());
- // _navigationService.pushPage(page: LoginScreen());
- } else {
- print("print login........");
- LoaderBottomSheet.hideLoader();
- _navigationService.pushPage(page: LoginScreen());
- }
- } catch (e) {
- log("Error processing IMEI registration response: $e");
- LoaderBottomSheet.hideLoader();
- _navigationService.pushPage(page: LoginScreen());
- }
- }, onError: (String error) {
+ if (!isInitialLoginNavigated) {
+ _navigationService.pushPage(page: SavedLogin());
+ }
+ // _navigationService.pushPage(page: LoginScreen());
+ } else {
+ if (isNavigate) {
+ LoaderBottomSheet.hideLoader();
+ _navigationService.pushPage(page: LoginScreen());
+ }
+ }
+ } catch (e) {
+ if (isNavigate) {
+ log("Error processing IMEI registration response: $e");
+ LoaderBottomSheet.hideLoader();
+ _navigationService.pushPage(page: LoginScreen());
+ }
+ }
+ }, onError: (String error) {
LoaderBottomSheet.hideLoader();
_dialogService.showErrorBottomSheet(message: error, onOkPressed: () {});
});
@@ -382,7 +403,7 @@ class AuthenticationViewModel extends ChangeNotifier {
mobileNumber: phoneNumber,
selectedLoginType: otpTypeEnum.toInt(),
zipCode: selectedCountrySignup.countryCode,
- nationalId:nationalIdOrFileNumber,
+ nationalId: nationalIdOrFileNumber,
isFileNo: isForRegister ? isPatientHasFile(request: payload) : false,
patientId: isFormFamilyFile ? _appState.getAuthenticatedUser()!.patientId : 0,
isForRegister: isForRegister,
@@ -612,13 +633,21 @@ class AuthenticationViewModel extends ChangeNotifier {
_appState.setSuperUserID = null;
_appState.setIsChildLoggedIn = false;
activation.list!.first.isParentUser = true;
+ var allUsersPayload = _appState.getUserSwitchPayloads;
+ if ((allUsersPayload.containsKey('superUser') && allUsersPayload['superUser'] != null)) {
+ _appState.addUserSwitchPayload(activation.list!.first.toJson(), isSuperUser: false);
+ } else {
+ _appState.addUserSwitchPayload(activation.list!.first.toJson(), isSuperUser: true);
+ }
} else {
_appState.setSuperUserID = _appState.getAuthenticatedUser()?.patientId;
_appState.setIsChildLoggedIn = true;
activation.list!.first.isParentUser = false;
+ _appState.addUserSwitchPayload(activation.list!.first.toJson(), isSuperUser: false);
}
} else {
activation.list!.first.isParentUser = true;
+ _appState.addUserSwitchPayload(activation.list!.first.toJson(), isSuperUser: true);
}
activation.list!.first.bloodGroup = activation.patientBloodType;
activation.list!.first.zipCode = selectedCountrySignup == CountryEnum.others ? '0' : selectedCountrySignup.countryCode;
@@ -627,7 +656,7 @@ class AuthenticationViewModel extends ChangeNotifier {
_appState.setUserBloodGroup = activation.patientBlodType ?? "N/A";
// Fetch patient blood type from new API
- await getPatientBloodTypeNew();
+ // await getPatientBloodTypeNew();
}
// _appState.setUserBloodGroup = (activation.patientBlodType ?? "");
_appState.setAppAuthToken = activation.authenticationTokenId;
@@ -719,6 +748,8 @@ class AuthenticationViewModel extends ChangeNotifier {
}
Future navigateToHomeScreen() async {
+ setIsInitialLoginNavigated(true);
+ getPatientBloodTypeNew();
Navigator.pushAndRemoveUntil(
_navigationService.navigatorKey.currentContext!,
CustomPageRoute(
@@ -862,17 +893,17 @@ class AuthenticationViewModel extends ChangeNotifier {
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.data is String) {
//TODO: This Section Need to Be Testing.
- LoadingUtils.hideFullScreenLoader();
+ LoaderBottomSheet.hideLoader();
_dialogService.showExceptionBottomSheet(message: apiResponse.data, onOkPressed: () {}, onCancelPressed: () {});
//TODO: Here We Need to Show a Dialog Of Something in the case of Fail With OK and Cancel and the Display Variable WIll be result.
} else {
- LoadingUtils.hideFullScreenLoader();
+ LoaderBottomSheet.hideLoader();
if (apiResponse.data["MessageStatus"] == 1) {
- LoadingUtils.showFullScreenLoader(isSuccessDialog: true, loadingText: "Your medical file has been created successfully. \nPlease proceed to login.");
+ LoaderBottomSheet.showLoader(loadingText: "Your medical file has been created successfully. \nPlease proceed to login.");
//TODO: Here We Need to Show a Dialog Of Something in the case of Success.
await clearDefaultInputValues(); // This will Clear All Default Values Of User.
Future.delayed(Duration(seconds: 1), () {
- LoadingUtils.hideFullScreenLoader();
+ LoaderBottomSheet.hideLoader();
// _navigationService.pushAndReplace(AppRoutes.loginScreen);
_navigationService.pushAndRemoveUntil(CustomPageRoute(page: LandingNavigation()), (r) => false);
_navigationService.push(CustomPageRoute(page: LoginScreen()));
@@ -1015,7 +1046,9 @@ class AuthenticationViewModel extends ChangeNotifier {
nationalId: _appState.getAuthenticatedUser()!.patientIdentificationNo,
gender: _appState.getAuthenticatedUser()!.gender)
.toJson());
- resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
+ resultEither.fold((failure) async {
+ // await _errorHandlerService.handleError(failure: failure);
+ }, (apiResponse) async {
if (apiResponse.messageStatus == 2) {}
if (apiResponse.messageStatus == 1) {
log("Insert Device Data Success");
@@ -1149,6 +1182,7 @@ class AuthenticationViewModel extends ChangeNotifier {
_appState.setVidaPlusProjectList([]);
_appState.setHMCProjectList([]);
_appState.setProjectsDetailList([]);
+ _appState.clearUserSwitchPayloads();
await clearDefaultInputValues();
_navigationService.pushAndReplace(AppRoutes.landingScreen);
} catch (e) {
@@ -1168,7 +1202,7 @@ class AuthenticationViewModel extends ChangeNotifier {
(apiResponse) {
if (apiResponse.messageStatus == 1 && apiResponse.data != null) {
// Extract blood type from response
- String? bloodType = apiResponse.data['GetPatientBloodType'];
+ String? bloodType = apiResponse.data['GetPatientBloodType'][0]["BloodType"];
if (bloodType != null && bloodType.isNotEmpty) {
_appState.setUserBloodGroup = bloodType;
log("Blood type updated from new API: $bloodType");
diff --git a/lib/features/blood_donation/widgets/hospital_selection.dart b/lib/features/blood_donation/widgets/hospital_selection.dart
index f49b42d6..c7313228 100644
--- a/lib/features/blood_donation/widgets/hospital_selection.dart
+++ b/lib/features/blood_donation/widgets/hospital_selection.dart
@@ -26,7 +26,7 @@ class HospitalBottomSheetBodySelection extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- if(!isHideTitle) LocaleKeys.selectHospital.tr(context: context).toText16(weight: FontWeight.w500, color: AppColors.greyTextColor),
+ if(!isHideTitle) LocaleKeys.selectHospital.tr(context: context).toText16(isBold: true, color: AppColors.greyTextColor),
SizedBox(height: 16.h),
SizedBox(
height: MediaQuery.sizeOf(context).height * .4,
@@ -75,7 +75,8 @@ class HospitalBottomSheetBodySelection extends StatelessWidget {
children: [
Utils.buildSvgWithAssets(icon: AppAssets.hmg).paddingOnly(right: 10),
Expanded(
- child: Text(hospital.projectName ?? "", style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16.f, color: AppColors.blackColor)),
+ child: (hospital.projectName ?? "").toString().toText16(isBold: true, color: AppColors.blackColor),
+ // Text(hospital.projectName ?? "", style: TextStyle(isBold: true, fontSize: 16.f, color: AppColors.blackColor)),
)
],
);
diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart
index afc9b568..6f2092f9 100644
--- a/lib/features/book_appointments/book_appointments_view_model.dart
+++ b/lib/features/book_appointments/book_appointments_view_model.dart
@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
+import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
@@ -11,6 +12,7 @@ import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/LaserCategoryType.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.dart';
@@ -139,6 +141,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool applyFilters = false;
bool isWaitingAppointmentAvailable = false;
+ bool isPatientRescheduleAppointment = false;
bool isWaitingAppointmentSelected = false;
int waitingAppointmentProjectID = 0;
@@ -150,6 +153,72 @@ class BookAppointmentsViewModel extends ChangeNotifier {
AppointmentNearestGateResponseModel? appointmentNearestGateResponseModel;
///variables for laser clinic
+ bool isLaserHospitalsLoading = false;
+ List laserHospitalsList = [];
+ int laserHospitalHmgCount = 0;
+ int laserHospitalHmcCount = 0;
+
+ Future getLaserHospitals({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ isLaserHospitalsLoading = true;
+ laserHospitalsList.clear();
+ laserHospitalHmgCount = 0;
+ laserHospitalHmcCount = 0;
+ notifyListeners();
+
+ final result = await bookAppointmentsRepo.getDoctorsList(253, 0, false, 0, '');
+
+ result.fold(
+ (failure) async {
+ isLaserHospitalsLoading = false;
+ notifyListeners();
+ onError?.call(failure.message);
+ },
+ (apiResponse) async {
+ if (apiResponse.messageStatus == 1) {
+ var doctorList = apiResponse.data!;
+ var regionList = await DoctorMapper.getMappedDoctor(
+ doctorList,
+ isArabic: _appState.isArabic(),
+ lat: _appState.userLat,
+ long: _appState.userLong,
+ );
+
+ var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0);
+ regionList = await DoctorMapper.sortList(isLocationEnabled, regionList);
+
+ // Flatten all hospitals across all regions into a single list
+ laserHospitalsList.clear();
+ Set addedHospitals = {};
+ regionList.registeredDoctorMap?.forEach((region, regionData) {
+ for (var hospital in regionData?.hmgDoctorList ?? []) {
+ if (!addedHospitals.contains(hospital.filterName)) {
+ addedHospitals.add(hospital.filterName ?? '');
+ laserHospitalsList.add(hospital);
+ }
+ }
+ for (var hospital in regionData?.hmcDoctorList ?? []) {
+ if (!addedHospitals.contains(hospital.filterName)) {
+ addedHospitals.add(hospital.filterName ?? '');
+ laserHospitalsList.add(hospital);
+ }
+ }
+ });
+
+ laserHospitalHmgCount = laserHospitalsList.where((h) => h.isHMC != true).length;
+ laserHospitalHmcCount = laserHospitalsList.where((h) => h.isHMC == true).length;
+
+ isLaserHospitalsLoading = false;
+ notifyListeners();
+ onSuccess?.call(apiResponse);
+ } else {
+ isLaserHospitalsLoading = false;
+ notifyListeners();
+ onError?.call(apiResponse.errorMessage ?? 'Unknown error');
+ }
+ },
+ );
+ }
+
List femaleLaserCategory = [
LaserCategoryType(1, 'bodyString'),
LaserCategoryType(2, 'face'),
@@ -157,7 +226,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
LaserCategoryType(11, 'retouch'),
];
List maleLaserCategory = [
- LaserCategoryType(1, 'body'),
+ LaserCategoryType(1, 'bodyString'),
LaserCategoryType(2, 'face'),
LaserCategoryType(11, 'retouch'),
];
@@ -262,6 +331,42 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
+ void filterDoctors(String query) {
+ if (query.isEmpty) {
+ filteredDoctorList = List.from(doctorsList);
+ _groupDoctorsList();
+ setIsNearestAppointmentSelected(isNearestAppointmentSelected);
+ notifyListeners();
+ return;
+ }
+
+ filteredDoctorList = doctorsList.where((doctor) {
+ final name = (doctor.name ?? '').toLowerCase();
+ final clinicName = (doctor.clinicName ?? '').toLowerCase();
+ final projectName = (doctor.projectName ?? '').toLowerCase();
+ final q = query.toLowerCase();
+ return name.contains(q) || clinicName.contains(q) || projectName.contains(q);
+ }).toList();
+
+ // Re-group filtered list
+ final clinicMap = >{};
+ final hospitalMap = >{};
+
+ for (var doctor in filteredDoctorList) {
+ final clinicKey = (doctor.clinicName ?? 'Unknown').trim();
+ clinicMap.putIfAbsent(clinicKey, () => []).add(doctor);
+
+ final hospitalKey = (doctor.projectName ?? 'Unknown').trim();
+ hospitalMap.putIfAbsent(hospitalKey, () => []).add(doctor);
+ }
+
+ doctorsListByClinic = clinicMap.values.toList();
+ doctorsListByHospital = hospitalMap.values.toList();
+ doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
+
+ notifyListeners();
+ }
+
void filterClinics(String? query) {
if (query!.isEmpty) {
_filteredClinicsList = List.from(clinicsList);
@@ -289,14 +394,19 @@ class BookAppointmentsViewModel extends ChangeNotifier {
isContinueDentalPlan = false;
isChiefComplaintsListLoading = true;
isWaitingAppointmentSelected = false;
+ isPatientRescheduleAppointment = false;
bodyTypes = [maleLaserCategory, femaleLaserCategory];
// getLocation();
notifyListeners();
}
+ refreshDoctorsList() {
+ setIsDoctorsListLoading(true);
+ getDoctorsList(isNearest: isNearestAppointmentSelected);
+ }
+
setIsNearestAppointmentSelected(bool isNearestAppointmentSelected) {
this.isNearestAppointmentSelected = isNearestAppointmentSelected;
-
if (isNearestAppointmentSelected) {
for (var group in doctorsListGrouped) {
group.sort((a, b) {
@@ -427,6 +537,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
+ setIsPatientRescheduleAppointment(bool isPatientRescheduleAppointment) {
+ this.isPatientRescheduleAppointment = isPatientRescheduleAppointment;
+ notifyListeners();
+ }
+
void onTabChanged(int index) {
calculationID = null;
isGetDocForHealthCal = false;
@@ -535,8 +650,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
//TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName
- Future getDoctorsList({int projectID = 0, bool isNearest = true, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ Future getDoctorsList({int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
+ filteredDoctorList.clear();
+ doctorsListGrouped.clear();
+ notifyListeners();
projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
final result =
await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, doctorName.isNotEmpty ? false : isNearest, doctorId, doctorName, isContinueDentalPlan: isContinueDentalPlan);
@@ -560,7 +678,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
clearSearchFilters();
getFiltersFromDoctorList();
_groupDoctorsList();
- setIsNearestAppointmentSelected(true);
+ setIsNearestAppointmentSelected(isNearest);
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@@ -652,12 +770,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
// Check if doctor is favorite after getting profile
- checkIsFavouriteDoctor(
- patientID: _appState.getAuthenticatedUser()!.patientId!,
- projectID: doctorsProfileResponseModel.projectID ?? 0,
- clinicID: doctorsProfileResponseModel.clinicID ?? 0,
- doctorID: doctorsProfileResponseModel.doctorID ?? 0,
- );
+ if(_appState.isAuthenticated) {
+ checkIsFavouriteDoctor(
+ patientID: _appState.getAuthenticatedUser()!.patientId!,
+ projectID: doctorsProfileResponseModel.projectID ?? 0,
+ clinicID: doctorsProfileResponseModel.clinicID ?? 0,
+ doctorID: doctorsProfileResponseModel.doctorID ?? 0,
+ );
+ }
if (onSuccess != null) {
onSuccess(apiResponse);
@@ -741,8 +861,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
initialSlotDuration = apiResponse.data["InitialSlotDuration"];
freeSlotsResponse = apiResponse.data['FreeTimeSlots'];
- isWaitingAppointmentAvailable = apiResponse.data["IsAllowToBookWaitingAppointment"];
- // isWaitingAppointmentAvailable = true;
+ // isWaitingAppointmentAvailable = apiResponse.data["IsAllowToBookWaitingAppointment"];
+ isWaitingAppointmentAvailable = true;
freeSlotsResponse.forEach((element) {
// date = (isLiveCareSchedule != null && isLiveCareSchedule)
@@ -852,64 +972,114 @@ class BookAppointmentsViewModel extends ChangeNotifier {
print(failure);
onError!(failure.message);
},
- (apiResponse) {
+ (apiResponse) async {
if (apiResponse.messageStatus == 2) {
// onError!(apiResponse);
- LoadingUtils.hideFullScreenLoader();
- showCommonBottomSheetWithoutHeight(
- title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
- navigationService.navigatorKey.currentContext!,
- child: Utils.getWarningWidget(
- loadingText: apiResponse.data["ErrorEndUserMessage"],
- isShowActionButtons: true,
- onCancelTap: () {
- navigationService.pop();
- },
- onConfirmTap: () async {
- navigationService.pop();
- PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
- appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
- clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
- projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
- endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
- startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
- doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
- isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
- originalClinicID: 0,
- originalProjectID: 0,
- appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
- );
-
- LoaderBottomSheet.showLoader(
- loadingText: LocaleKeys.reschedulingAppo.tr(context: navigationService.navigatorKey.currentContext!),
- );
- await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
+ if (isPatientRescheduleAppointment) {
+ PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
+ appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
+ clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
+ projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
+ endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
+ startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
+ doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
+ isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
+ originalClinicID: 0,
+ originalProjectID: 0,
+ appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
+ );
+
+ await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
+ // LoaderBottomSheet.hideLoader();
+ Future.delayed(Duration(milliseconds: 50)).then((value) async {});
+ // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
+ await insertSpecificAppointment(
+ onError: (err) {},
+ onSuccess: (apiResp) async {
LoaderBottomSheet.hideLoader();
- Future.delayed(Duration(milliseconds: 50)).then((value) async {});
- LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: LocaleKeys.bookingYourAppointment.tr());
- await insertSpecificAppointment(
- onError: (err) {},
- onSuccess: (apiResp) async {
- LoadingUtils.hideFullScreenLoader();
- await Future.delayed(Duration(milliseconds: 50)).then((value) async {
- LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
- await Future.delayed(Duration(milliseconds: 4000)).then((value) {
- LoadingUtils.hideFullScreenLoader();
- Navigator.pushAndRemoveUntil(
- navigationService.navigatorKey.currentContext!,
- CustomPageRoute(
- page: LandingNavigation(),
- ),
- (r) => false);
+ await Future.delayed(Duration(milliseconds: 50)).then((value) async {
+ showCommonBottomSheetWithoutHeight(
+ GetIt.instance().navigatorKey.currentContext!,
+ child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
+ callBackFunc: () {
+ setIsPatientRescheduleAppointment(false);
+ Navigator.pushAndRemoveUntil(
+ navigationService.navigatorKey.currentContext!,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
+ },
+ isFullScreen: false,
+ isCloseButtonVisible: false,
+ isAutoDismiss: true
+ );
+ });
+ });
+ });
+ } else {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
+ navigationService.navigatorKey.currentContext!,
+ child: Utils.getWarningWidget(
+ loadingText: apiResponse.data["ErrorEndUserMessage"],
+ isShowActionButtons: true,
+ onCancelTap: () {
+ navigationService.pop();
+ },
+ onConfirmTap: () async {
+ navigationService.pop();
+ PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
+ appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
+ clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
+ projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
+ endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
+ startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
+ doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
+ isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
+ originalClinicID: 0,
+ originalProjectID: 0,
+ appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
+ );
+
+ LoaderBottomSheet.showLoader(
+ loadingText: LocaleKeys.reschedulingAppo.tr(context: navigationService.navigatorKey.currentContext!),
+ );
+ await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
+ LoaderBottomSheet.hideLoader();
+ Future.delayed(Duration(milliseconds: 50)).then((value) async {});
+ LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
+ await insertSpecificAppointment(
+ onError: (err) {},
+ onSuccess: (apiResp) async {
+ LoaderBottomSheet.hideLoader();
+ await Future.delayed(Duration(milliseconds: 50)).then((value) async {
+ showCommonBottomSheetWithoutHeight(
+ GetIt.instance().navigatorKey.currentContext!,
+ child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
+ callBackFunc: () {
+ setIsPatientRescheduleAppointment(false);
+ Navigator.pushAndRemoveUntil(
+ navigationService.navigatorKey.currentContext!,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
+ },
+ isFullScreen: false,
+ isCloseButtonVisible: false,
+ isAutoDismiss: true
+ );
});
});
- });
- });
- }),
- callBackFunc: () {},
- isFullScreen: false,
- isCloseButtonVisible: true,
- );
+ });
+ }),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ }
} else if (apiResponse.messageStatus == 1) {
if (apiResponse.data == null || apiResponse.data!.isEmpty) {
onError!("No free slots available".tr());
@@ -946,7 +1116,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// onError!(apiResponse);
- LoadingUtils.hideFullScreenLoader();
+ LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
@@ -982,22 +1152,28 @@ class BookAppointmentsViewModel extends ChangeNotifier {
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
navigationService.pop();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
- LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: LocaleKeys.bookingYourAppointment.tr());
+ LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr());
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
- LoadingUtils.hideFullScreenLoader();
+ LoaderBottomSheet.hideLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
- LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
- await Future.delayed(Duration(milliseconds: 4000)).then((value) {
- LoadingUtils.hideFullScreenLoader();
- Navigator.pushAndRemoveUntil(
- navigationService.navigatorKey.currentContext!,
- CustomPageRoute(
- page: LandingNavigation(),
- ),
- (r) => false);
- });
+ showCommonBottomSheetWithoutHeight(
+ GetIt.instance().navigatorKey.currentContext!,
+ child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h),
+ callBackFunc: () {
+ setIsPatientRescheduleAppointment(false);
+ Navigator.pushAndRemoveUntil(
+ navigationService.navigatorKey.currentContext!,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
+ },
+ isFullScreen: false,
+ isCloseButtonVisible: false,
+ isAutoDismiss: true
+ );
});
});
});
@@ -1491,7 +1667,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
- LoadingUtils.hideFullScreenLoader();
+ LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
diff --git a/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart b/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart
index 75ed1229..a9aa273e 100644
--- a/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart
+++ b/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart
@@ -193,7 +193,7 @@ class DoctorsListResponseModel {
serviceID = json['ServiceID'];
setupID = json['SetupID'];
speciality = json['Speciality'] != null ? json['Speciality'].cast() : [];
- // specialityN = json['SpecialityN'].cast();
+ specialityN = json['SpecialityN'] != null ? json['SpecialityN'].cast() : [];
transactionType = json['TransactionType'];
virtualEmploymentType = json['VirtualEmploymentType'];
workingHours = json['WorkingHours'];
diff --git a/lib/features/book_appointments/models/resp_models/laser_body_parts.dart b/lib/features/book_appointments/models/resp_models/laser_body_parts.dart
index d7d3fbd1..dcb8a70e 100644
--- a/lib/features/book_appointments/models/resp_models/laser_body_parts.dart
+++ b/lib/features/book_appointments/models/resp_models/laser_body_parts.dart
@@ -61,4 +61,14 @@ class LaserBodyPart {
data['CategoryNameN'] = this.categoryNameN;
return data;
}
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is LaserBodyPart &&
+ runtimeType == other.runtimeType &&
+ id == other.id;
+
+ @override
+ int get hashCode => id.hashCode;
}
diff --git a/lib/features/emergency_services/emergency_services_view_model.dart b/lib/features/emergency_services/emergency_services_view_model.dart
index ec211793..bfe226f8 100644
--- a/lib/features/emergency_services/emergency_services_view_model.dart
+++ b/lib/features/emergency_services/emergency_services_view_model.dart
@@ -530,6 +530,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
void setTransportationOption(PatientERTransportationMethod item) {
selectedTransportOption = item;
+ notifyListeners();
}
void updateCallingPlace(AmbulanceCallingPlace? value) {
diff --git a/lib/features/hmg_services/hmg_services_repo.dart b/lib/features/hmg_services/hmg_services_repo.dart
index 89285339..b70e024c 100644
--- a/lib/features/hmg_services/hmg_services_repo.dart
+++ b/lib/features/hmg_services/hmg_services_repo.dart
@@ -919,8 +919,6 @@ class HmgServicesRepoImp implements HmgServicesRepo {
@override
Future>>> getPatientVitalSign() async {
Map requestBody = {
-
-
};
try {
diff --git a/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.dart b/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.dart
index 66f5d77b..90c513c4 100644
--- a/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.dart
+++ b/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.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:hmg_patient_app_new/theme/colors.dart';
/// UI-only helper model for Vital Sign cards.
@@ -49,7 +51,7 @@ class VitalSignUiModel {
}
// Warning for both low and overweight/underweight BMI, since they can indicate potential health issues.
- if (s.contains('low') || s.contains('underweight') || s.contains('overweight')) {
+ if (s.contains('low') || s.contains("OverWeight") || s.contains("وزن زائد") || s.contains("UnderWeight") || s.contains("وزن ناقص")) {
final Color yellowBg = AppColors.highAndLow.withValues(alpha: 0.12);
return VitalSignUiModel(
iconBg: yellowBg,
@@ -96,13 +98,13 @@ class VitalSignUiModel {
final double bmiResult = double.tryParse(bmi.toString()) ?? 0;
if (bmiResult >= 30) {
- bmiStatus = "High";
+ bmiStatus = LocaleKeys.obese.tr();
} else if (bmiResult < 30 && bmiResult >= 25) {
- bmiStatus = "Overweight";
+ bmiStatus = LocaleKeys.overWeight.tr();
} else if (bmiResult < 25 && bmiResult >= 18.5) {
- bmiStatus = "Normal";
+ bmiStatus = LocaleKeys.normal.tr();
} else if (bmiResult < 18.5) {
- bmiStatus = "Underweight";
+ bmiStatus = LocaleKeys.underWeight.tr();
}
// if (bmi == null) return 'N/A';
diff --git a/lib/features/immediate_livecare/immediate_livecare_repo.dart b/lib/features/immediate_livecare/immediate_livecare_repo.dart
index cbce4ea2..8775378b 100644
--- a/lib/features/immediate_livecare/immediate_livecare_repo.dart
+++ b/lib/features/immediate_livecare/immediate_livecare_repo.dart
@@ -5,6 +5,7 @@ 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/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
+import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_clinic_schedule_response_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_fees_response_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_patient_livecare_history_response_model.dart';
@@ -13,6 +14,9 @@ import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class ImmediateLiveCareRepo {
Future>>> getLiveCareImmediateClinicsList(int age, int genderID, {Function(dynamic)? onSuccess, Function(String)? onError});
+ Future>>> getLiveCareImmediateClinicSchedule(int age, int genderID, int serviceID,
+ {Function(dynamic)? onSuccess, Function(String)? onError});
+
Future>> getLiveCareImmediateAppointmentFees(int age, int genderID, int serviceID,
{Function(dynamic)? onSuccess, Function(String)? onError});
@@ -203,4 +207,47 @@ class ImmediateLiveCareRepoImp implements ImmediateLiveCareRepo {
return Left(UnknownFailure(e.toString()));
}
}
+
+ @override
+ Future>>> getLiveCareImmediateClinicSchedule(int age, int genderID, int serviceID,
+ {Function(dynamic p1)? onSuccess, Function(String p1)? onError}) async {
+ Map mapDevice = {
+ "Age": age,
+ "Gender": genderID,
+ "ServiceID": serviceID,
+ };
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_LIVECARE_CLINIC_TIMING,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ // onError!(error);
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final respObject = response['PatientER_GetClinicsServiceTimingsList'];
+ final liveCareScheduleObj = respObject.map((item) => LiveCareClinicScheduleResponse.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: liveCareScheduleObj,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
}
diff --git a/lib/features/immediate_livecare/immediate_livecare_view_model.dart b/lib/features/immediate_livecare/immediate_livecare_view_model.dart
index 46f5a652..6613b481 100644
--- a/lib/features/immediate_livecare/immediate_livecare_view_model.dart
+++ b/lib/features/immediate_livecare/immediate_livecare_view_model.dart
@@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart';
+import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_clinic_schedule_response_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_fees_response_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_patient_livecare_history_response_model.dart';
@@ -46,6 +47,8 @@ class ImmediateLiveCareViewModel extends ChangeNotifier {
List patientLiveCareHistoryList = [];
bool patientHasPendingLiveCareRequest = false;
+ List liveCareClinicScheduleList = [];
+
late AppState _appState;
Duration countdownDuration = Duration(minutes: 1, seconds: 0);
@@ -265,6 +268,33 @@ class ImmediateLiveCareViewModel extends ChangeNotifier {
);
}
+ Future getLiveCareImmediateClinicSchedule({int? serviceID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ liveCareClinicScheduleList.clear();
+ notifyListeners();
+
+ final result =
+ await immediateLiveCareRepo.getLiveCareImmediateClinicSchedule(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, serviceID!);
+
+ result.fold(
+ (failure) async => await errorHandlerService.handleError(failure: failure),
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ } else if (apiResponse.messageStatus == 1) {
+ liveCareClinicScheduleList = apiResponse.data!;
+
+ // immediateLiveCareClinicsList.sort((a, b) => b.isOnline!.compareTo(a.isOnline!));
+
+ // isImmediateLiveCareClinicsLoading = false;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+
void startTimer() {
timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime());
notifyListeners();
diff --git a/lib/features/immediate_livecare/models/resp_models/get_livecare_clinic_schedule_response_model.dart b/lib/features/immediate_livecare/models/resp_models/get_livecare_clinic_schedule_response_model.dart
new file mode 100644
index 00000000..787167de
--- /dev/null
+++ b/lib/features/immediate_livecare/models/resp_models/get_livecare_clinic_schedule_response_model.dart
@@ -0,0 +1,71 @@
+class LiveCareClinicScheduleResponse {
+ int? iD;
+ int? serviceID;
+ int? dayOfWeek;
+ String? dayOfWeekStr;
+ bool? isActive;
+ String? createdOn;
+ String? createdBy;
+ bool? projectOutSA;
+ String? dayOfWeekStrN;
+ List? shiftTimings;
+
+ LiveCareClinicScheduleResponse({this.iD, this.serviceID, this.dayOfWeek, this.dayOfWeekStr, this.isActive, this.createdOn, this.createdBy, this.projectOutSA, this.dayOfWeekStrN, this.shiftTimings});
+
+ LiveCareClinicScheduleResponse.fromJson(Map json) {
+ iD = json['ID'];
+ serviceID = json['ServiceID'];
+ dayOfWeek = json['DayOfWeek'];
+ dayOfWeekStr = json['DayOfWeekStr'];
+ isActive = json['IsActive'];
+ createdOn = json['CreatedOn'];
+ createdBy = json['CreatedBy'];
+ projectOutSA = json['ProjectOutSA'];
+ dayOfWeekStrN = json['DayOfWeekStrN'];
+ if (json['ShiftTimings'] != null) {
+ shiftTimings = [];
+ json['ShiftTimings'].forEach((v) {
+ shiftTimings!.add(new ShiftTimings.fromJson(v));
+ });
+ }
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['ID'] = this.iD;
+ data['ServiceID'] = this.serviceID;
+ data['DayOfWeek'] = this.dayOfWeek;
+ data['DayOfWeekStr'] = this.dayOfWeekStr;
+ data['IsActive'] = this.isActive;
+ data['CreatedOn'] = this.createdOn;
+ data['CreatedBy'] = this.createdBy;
+ data['ProjectOutSA'] = this.projectOutSA;
+ data['DayOfWeekStrN'] = this.dayOfWeekStrN;
+ if (this.shiftTimings != null) {
+ data['ShiftTimings'] = this.shiftTimings!.map((v) => v.toJson()).toList();
+ }
+ return data;
+ }
+}
+
+class ShiftTimings {
+ String? endTime;
+ int? shiftID;
+ String? startTime;
+
+ ShiftTimings({this.endTime, this.shiftID, this.startTime});
+
+ ShiftTimings.fromJson(Map json) {
+ endTime = json['EndTime'];
+ shiftID = json['ShiftID'];
+ startTime = json['StartTime'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['EndTime'] = this.endTime;
+ data['ShiftID'] = this.shiftID;
+ data['StartTime'] = this.startTime;
+ return data;
+ }
+}
diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart
index 0e7efdb3..6835badb 100644
--- a/lib/features/lab/lab_view_model.dart
+++ b/lib/features/lab/lab_view_model.dart
@@ -4,6 +4,7 @@ import 'dart:core';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
@@ -71,6 +72,9 @@ class LabViewModel extends ChangeNotifier {
List uniqueTestsList = [];
List indexedCharacterForUniqueTest = [];
+ List filteredUniqueTestsList = [];
+ List filteredIndexedCharacterForUniqueTest = [];
+
double maxY = 0.0;
double minY = double.infinity;
double maxX = double.infinity;
@@ -84,6 +88,9 @@ class LabViewModel extends ChangeNotifier {
LabOrderResponseByAi? labOrderResponseByAi;
LabOrdersResponseByAi? labOrdersResponseByAi;
+ bool isLabAIAnalysisNeedsToBeShown = true;
+ bool isLabResultsHistoryShowMore = false;
+
LabViewModel({required this.labRepo, required this.errorHandlerService, required this.navigationService});
initLabProvider() {
@@ -93,6 +100,8 @@ class LabViewModel extends ChangeNotifier {
labOrderTests.clear();
isLabOrdersLoading = true;
isLabResultsLoading = true;
+ isLabAIAnalysisNeedsToBeShown = true;
+ isLabResultsHistoryShowMore = false;
patientLabOrdersByClinic.clear();
patientLabOrdersByHospital.clear();
patientLabOrdersViewList.clear();
@@ -111,6 +120,16 @@ class LabViewModel extends ChangeNotifier {
notifyListeners();
}
+ setIsLabAIAnalysisNeedsToBeShown(bool isLabAIAnalysisNeedsToBeShown) {
+ this.isLabAIAnalysisNeedsToBeShown = isLabAIAnalysisNeedsToBeShown;
+ notifyListeners();
+ }
+
+ setIsLabResultsHistoryShowMore() {
+ isLabResultsHistoryShowMore = !isLabResultsHistoryShowMore;
+ notifyListeners();
+ }
+
void setIsSortByClinic(bool value) {
isSortByClinic = value;
patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
@@ -218,7 +237,8 @@ class LabViewModel extends ChangeNotifier {
final clinicMap = >{};
final hospitalMap = >{};
if (query.isEmpty) {
- // filteredLabOrders = List.from(patientLabOrders); // reset
+ filteredLabOrders = List.from(patientLabOrders); // reset
+ filteredUniqueTestsList = List.from(uniqueTestsList);
for (var order in patientLabOrders) {
final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
clinicMap.putIfAbsent(clinicKey, () => []).add(order);
@@ -234,7 +254,10 @@ class LabViewModel extends ChangeNotifier {
final descriptions = order.testDetails?.map((d) => d.description?.toLowerCase()).toList() ?? [];
return descriptions.any((desc) => desc != null && desc.contains(query.toLowerCase()));
}).toList();
- // patientLabOrders = filteredLabOrders;
+ filteredUniqueTestsList = uniqueTestsList.where((test) {
+ final desc = test.description?.toLowerCase() ?? '';
+ return desc.contains(query.toLowerCase());
+ }).toList();
for (var order in filteredLabOrders) {
final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
clinicMap.putIfAbsent(clinicKey, () => []).add(order);
@@ -246,6 +269,14 @@ class LabViewModel extends ChangeNotifier {
patientLabOrdersByHospital = hospitalMap.values.toList();
patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
}
+ // Rebuild filtered indexed characters
+ filteredIndexedCharacterForUniqueTest = [];
+ for (var test in filteredUniqueTestsList) {
+ String label = test.description ?? "";
+ if (label.isEmpty) continue;
+ if (filteredIndexedCharacterForUniqueTest.contains(label[0].toLowerCase())) continue;
+ filteredIndexedCharacterForUniqueTest.add(label[0].toLowerCase());
+ }
notifyListeners();
}
@@ -280,6 +311,10 @@ class LabViewModel extends ChangeNotifier {
for (var element in uniqueTests) {
labOrderTests.add(element.description ?? "");
}
+
+ // Initialize filtered lists with full data
+ filteredUniqueTestsList = List.from(uniqueTestsList);
+ filteredIndexedCharacterForUniqueTest = List.from(indexedCharacterForUniqueTest);
}
Future getLabResultsByAppointmentNo(
@@ -389,15 +424,17 @@ class LabViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) {
var sortedResult = sort(apiResponse.data!);
var recentThree = sortedResult.take(3).toList();
- mainLabResults = recentThree;
+ mainLabResults = sortedResult;
double highRefrenceValue = double.negativeInfinity;
String? flagForHighReferenceRange;
double lowRefenceValue = double.infinity;
String? flagForLowReferenceRange;
- sortedResult.take(3).toList().reversed.forEach((element) {
+ sortedResult.toList().reversed.forEach((element) {
try {
+ shouldShowGraph = isNumeric(element.resultValue!);
+ // if(isNumeric(element.resultValue!)){
var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
var resultValue = double.parse(element.resultValue!);
// var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag ?? "");
@@ -409,13 +446,22 @@ class LabViewModel extends ChangeNotifier {
if (resultValue < minY) {
minY = resultValue;
}
- if (highRefrenceValue < double.parse(element.referenceHigh ?? "0.0")) {
+ // if (highRefrenceValue < double.parse(element.referenceHigh ?? "0.0")) {
highRefrenceValue = double.parse(element.referenceHigh ?? "0.0");
flagForHighReferenceRange = element.calculatedResultFlag;
- }
- if (lowRefenceValue > double.parse(element.referenceLow ?? "0.0")) {
+ // }
+ // if (lowRefenceValue > double.parse(element.referenceLow ?? "0.0")) {
lowRefenceValue = double.parse(element.referenceLow ?? "0.0");
flagForLowReferenceRange = element.calculatedResultFlag;
+ // }
+
+ if(double.parse(element.referenceHigh ?? "0.0") == 0 ){
+ highRefrenceValue = double.negativeInfinity;
+ }
+
+
+ if(double.parse(element.referenceLow ?? "0.0") == 0 ){
+ lowRefenceValue = double.negativeInfinity;
}
filteredGraphValues.add(DataPoint(
@@ -427,28 +473,38 @@ class LabViewModel extends ChangeNotifier {
time: DateUtil.convertStringToDate(element.verifiedOnDateTime),
referenceValue: element.calculatedResultFlag ?? "",
));
- } catch (e) {}
+
+ } catch (e) {
+ }
});
if (flagForLowReferenceRange == null && flagForHighReferenceRange == null) {
highRefrenceValue = maxY;
lowRefenceValue = minY;
}
//
- if (minY > lowRefenceValue) {
+ if (lowRefenceValue != double.negativeInfinity &&minY > lowRefenceValue) {
minY = lowRefenceValue - getInterval();
+ }else if (highRefrenceValue != double.negativeInfinity && minY > highRefrenceValue) {
+ minY = highRefrenceValue - getInterval();
}
this.flagForHighReferenceRange = flagForHighReferenceRange;
this.flagForLowReferenceRange = flagForLowReferenceRange;
highTransformedReferenceValue = double.parse(transformValueInRange(highRefrenceValue, flagForHighReferenceRange ?? "").toStringAsFixed(1));
lowTransformedReferenceValue = double.parse(transformValueInRange(lowRefenceValue, flagForLowReferenceRange ?? "").toStringAsFixed(1));
- this.highRefrenceValue = double.parse(highRefrenceValue.toStringAsFixed(1));
- this.lowRefenceValue = double.parse(lowRefenceValue.toStringAsFixed(1));
- if(maxY getAiOverviewLabOrders({required PatientLabOrdersResponseModel labOrder, required String loadingText}) async {
// LoadingUtils.showFullScreenLoader(loadingText: "Loading and analysing your data,\nPlease be patient and let the AI do the magic. \nPlease be patient, This might take some time.");
- LoadingUtils.showFullScreenLoader(loadingText: loadingText);
+ // LoadingUtils.showFullScreenLoader(loadingText: loadingText);
+ isLabAIAnalysisNeedsToBeShown = true;
+ notifyListeners();
+
+ LoaderBottomSheet.showLoader(
+ loadingText: loadingText,
+ showCloseButton: true,
+ onCloseTap: () {
+ setIsLabAIAnalysisNeedsToBeShown(false);
+ });
List