Merge branch 'master' into dev_aamir

# Conflicts:
#	assets/langs/ar-SA.json
#	assets/langs/en-US.json
#	lib/features/authentication/authentication_view_model.dart
#	lib/generated/locale_keys.g.dart
pull/182/head
aamir-csol 2 months ago
commit a105d87241

@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgaPetsOq3tqUlswBN
BFUWIIdEg4dcTCGRUKbAEx8ViFygCgYIKoZIzj0DAQehRANCAAS6IKkZ4tCeBlN6
DtXiBekCiIbgHxWk8AWppP0H3mJ+I2s8cIEaquL4/yeLDcHzAHf8tHcYhF/TvggT
nc30yDLv
-----END PRIVATE KEY-----

Binary file not shown.

@ -320,6 +320,7 @@ internal class PenguinView(
if(clinicId.isEmpty()) return if(clinicId.isEmpty()) return
navigateTo(clinicId) navigateTo(clinicId)
// navigateTo("3-1")
} }
/** /**

@ -1496,10 +1496,6 @@
"someRemarksAboutPrescription": "ستجدون هنا بعض الملاحظات حول الوصفة الطبية", "someRemarksAboutPrescription": "ستجدون هنا بعض الملاحظات حول الوصفة الطبية",
"notifyMeBeforeConsumptionTime": "أبلغني قبل وقت الاستهلاك", "notifyMeBeforeConsumptionTime": "أبلغني قبل وقت الاستهلاك",
"noMedicationsToday": "لا أدوية اليوم", "noMedicationsToday": "لا أدوية اليوم",
"route": "Route: {route}",
"frequency": "Frequency: {frequency}",
"instruction": "Instruction: {instruction}",
"duration": "Duration: {days}",
"reminders": "تذكيرات", "reminders": "تذكيرات",
"reminderAddedToCalendar": "تمت إضافة تذكير إلى التقويم ✅", "reminderAddedToCalendar": "تمت إضافة تذكير إلى التقويم ✅",
"errorWhileSettingCalendar": "حدث خطأ أثناء ضبط التقويم:{error}", "errorWhileSettingCalendar": "حدث خطأ أثناء ضبط التقويم:{error}",
@ -1526,8 +1522,6 @@
"trackerAndOthers": "المتتبع وأخرى", "trackerAndOthers": "المتتبع وأخرى",
"bmi": "مؤشر كتلة الجسم", "bmi": "مؤشر كتلة الجسم",
"yesPleaseINeedHelp": "نعم، من فضلك أريد المساعدة", "yesPleaseINeedHelp": "نعم، من فضلك أريد المساعدة",
"noThanksIKnowTheClinic": "لا شكراً، أعرف العيادة",
"unableToSendOTP": "تعذر إرسال رمز التحقق لمرة واحدة",
"aiOverView": "AI ملخص", "aiOverView": "AI ملخص",
"generateAiAnalysis": "قم بإجراء تحليل لهذا الطلب AI", "generateAiAnalysis": "قم بإجراء تحليل لهذا الطلب AI",
"aiAnalysis": "AI تحليل", "aiAnalysis": "AI تحليل",
@ -1569,5 +1563,13 @@
"confirmYourLocation": "قم بتأكيد موقعك",
"confirmYourLocationDesc": "يرجى تأكيد موقعك الحالي لعرض الاتجاهات المناسبة.",
"insideHospital": "أنا داخل المستشفى",
"outsideHospital": "أنا خارج المستشفى",
"servicePriceList": "قائمة أسعار الخدمات",
"servicePriceListDesc": "توضح قائمة أسعار الخدمات أدناه رسوم الخدمات الصحية المقدمة للمرضى بنظام الدفع النقدي. أما فيما يتعلق بالخدمات المشمولة بالتأمين، فسيتم تطبيق التغطية التأمينية والتحقق من الأهلية واحتساب نسب التحمل وفقًا لشروط وثيقة التأمين وجدول المنافع المعتمد لكل شركة تأمين.",
"servicePriceListRights": "يحق للمريض الحصول على متابعة مجانية في غضون 14 يومًا من الزيارة الأولى",
"serviceName": "اسم الخدمة",
"noFreeSlot": "لا توجد مواعيد متاحة في التاريخ المحدد. يرجى اختيار تاريخ آخر أو إعادة المحاولة لاحقاً."
} }

@ -618,7 +618,7 @@
"general": "General", "general": "General",
"profile": "ملفي", "profile": "ملفي",
"notifications": "إشعارات", "notifications": "إشعارات",
"notificationDetails": "تفاصيل الاشعار", "notificationDetails": "Notification Details",
"notificationDetailsa": "تفاصيل الاشعار", "notificationDetailsa": "تفاصيل الاشعار",
"infoMyDoctorPoints": "", "infoMyDoctorPoints": "",
"infoMyDoctor": "This service allows you to see all the doctors you have visited in Al Habib Medical Group, and through this service:", "infoMyDoctor": "This service allows you to see all the doctors you have visited in Al Habib Medical Group, and through this service:",
@ -1475,7 +1475,6 @@
"remindersSet": "Reminders Set!", "remindersSet": "Reminders Set!",
"dailyWaterRemindersScheduledAt": "Daily water reminders scheduled at:", "dailyWaterRemindersScheduledAt": "Daily water reminders scheduled at:",
"waterConsumption": "Water Consumption", "waterConsumption": "Water Consumption",
"selectActivityLevel": "Select Activity Level",
"selectNumberOfReminders": "Select Number of Reminders", "selectNumberOfReminders": "Select Number of Reminders",
"h2oSettings": "H20 Settings", "h2oSettings": "H20 Settings",
"settingsSavedSuccessfully": "Settings saved successfully", "settingsSavedSuccessfully": "Settings saved successfully",
@ -1487,10 +1486,8 @@
"someRemarksAboutPrescription": "some remarks about the prescription will be here", "someRemarksAboutPrescription": "some remarks about the prescription will be here",
"notifyMeBeforeConsumptionTime": "Notify me before the consumption time", "notifyMeBeforeConsumptionTime": "Notify me before the consumption time",
"noMedicationsToday": "No medications today", "noMedicationsToday": "No medications today",
"route": "Route: {route}", "route": "Route",
"frequency": "Frequency: {frequency}", "frequency": "Frequency",
"instruction": "Instruction: {instruction}",
"duration": "Duration: {days}",
"reminders": "Reminders", "reminders": "Reminders",
"reminderAddedToCalendar": "Reminder added to calendar ✅", "reminderAddedToCalendar": "Reminder added to calendar ✅",
"errorWhileSettingCalendar": "Error while setting calendar: {error}", "errorWhileSettingCalendar": "Error while setting calendar: {error}",
@ -1517,6 +1514,15 @@
"trackerAndOthers": "Tracker & Others", "trackerAndOthers": "Tracker & Others",
"bmi": "BMI", "bmi": "BMI",
"yesPleaseINeedHelp": "Yes, Please I need help", "yesPleaseINeedHelp": "Yes, Please I need help",
"confirmYourLocation": "Confirm Your Location",
"confirmYourLocationDesc": "Please confirm your present location to view appropriate directions.",
"insideHospital": "I am inside the hospital",
"outsideHospital": "I am outside the hospital",
"servicePriceList": "Services Price List",
"servicePriceListDesc": "Below is the services price list outline the healthcare services fees for cash payments, where the insurance coverage, eligibility, and co-payment deductions will be processed in accordance with the insurance policy terms and the table of benefits of each insurance providers:",
"servicePriceListRights": "The patient has the right to a free follow-up within 14 days of initial visit",
"serviceName": "Service Name",
"noFreeSlot": "No free slot available for the selected date. Please select another date or check back later."
"noThanksIKnowTheClinic": "No, Thanks. I Know the clinic", "noThanksIKnowTheClinic": "No, Thanks. I Know the clinic",
"unableToSendOTP": "Unable to send Verification OTP", "unableToSendOTP": "Unable to send Verification OTP",
"aiOverView": "AI Overview", "aiOverView": "AI Overview",

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

@ -2,10 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CLIENT_ID</key>
<string>815750722565-da8p56le8bd6apsbm9eft0jjl1rtpgkt.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.815750722565-da8p56le8bd6apsbm9eft0jjl1rtpgkt</string>
<key>ANDROID_CLIENT_ID</key> <key>ANDROID_CLIENT_ID</key>
<string>815750722565-m14h8mkosm7cnq6uh6rhqr54dn02d705.apps.googleusercontent.com</string> <string>815750722565-m14h8mkosm7cnq6uh6rhqr54dn02d705.apps.googleusercontent.com</string>
<key>API_KEY</key> <key>API_KEY</key>
@ -15,11 +11,11 @@
<key>PLIST_VERSION</key> <key>PLIST_VERSION</key>
<string>1</string> <string>1</string>
<key>BUNDLE_ID</key> <key>BUNDLE_ID</key>
<string>com.HMG.HMG-Smartphone</string> <string>com.cloudsolutions.HMGPatientApp</string>
<key>PROJECT_ID</key> <key>PROJECT_ID</key>
<string>api-project-815750722565</string> <string>api-project-815750722565</string>
<key>STORAGE_BUCKET</key> <key>STORAGE_BUCKET</key>
<string>api-project-815750722565.appspot.com</string> <string>api-project-815750722565.firebasestorage.app</string>
<key>IS_ADS_ENABLED</key> <key>IS_ADS_ENABLED</key>
<false></false> <false></false>
<key>IS_ANALYTICS_ENABLED</key> <key>IS_ANALYTICS_ENABLED</key>
@ -31,7 +27,7 @@
<key>IS_SIGNIN_ENABLED</key> <key>IS_SIGNIN_ENABLED</key>
<true></true> <true></true>
<key>GOOGLE_APP_ID</key> <key>GOOGLE_APP_ID</key>
<string>1:815750722565:ios:328ec247a81a2ca23c186c</string> <string>1:815750722565:ios:34ec03c09e02e21f3c186c</string>
<key>DATABASE_URL</key> <key>DATABASE_URL</key>
<string>https://api-project-815750722565.firebaseio.com</string> <string>https://api-project-815750722565.firebaseio.com</string>
</dict> </dict>

@ -54,6 +54,8 @@ post_install do |installer|
'PERMISSION_EVENTS_FULL_ACCESS=1', 'PERMISSION_EVENTS_FULL_ACCESS=1',
## dart: PermissionGroup.reminders ## dart: PermissionGroup.reminders
'PERMISSION_REMINDERS=1', 'PERMISSION_REMINDERS=1',
## dart: PermissionGroup.notification
'PERMISSION_NOTIFICATIONS=1',
] ]
build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386'
build_configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' build_configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'

@ -18,12 +18,12 @@
61243B562EC5FA3700D46FA0 /* PenguinNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B432EC5FA3700D46FA0 /* PenguinNavigator.swift */; }; 61243B562EC5FA3700D46FA0 /* PenguinNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B432EC5FA3700D46FA0 /* PenguinNavigator.swift */; };
61243B572EC5FA3700D46FA0 /* PenguinViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B462EC5FA3700D46FA0 /* PenguinViewFactory.swift */; }; 61243B572EC5FA3700D46FA0 /* PenguinViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61243B462EC5FA3700D46FA0 /* PenguinViewFactory.swift */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
766D8CB62EC60BE600D05E07 /* Penguin.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB32EC60BE600D05E07 /* Penguin.xcframework */; }; 765A5A8C2F35CD8B0003FF7D /* Penguin.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 765A5A802F35CD730003FF7D /* Penguin.xcframework */; };
766D8CB72EC60BE600D05E07 /* Penguin.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB32EC60BE600D05E07 /* Penguin.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 765A5A8D2F35CD8B0003FF7D /* Penguin.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 765A5A802F35CD730003FF7D /* Penguin.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
766D8CB82EC60BE600D05E07 /* PenNavUI.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */; }; 765A5A8E2F35CD8B0003FF7D /* PenguinINRenderer.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 765A5A812F35CD730003FF7D /* PenguinINRenderer.xcframework */; };
766D8CB92EC60BE600D05E07 /* PenNavUI.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 765A5A8F2F35CD8B0003FF7D /* PenguinINRenderer.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 765A5A812F35CD730003FF7D /* PenguinINRenderer.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
766D8CBA2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */; }; 765A5A902F35CD8B0003FF7D /* PenNavUI.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 765A5A822F35CD730003FF7D /* PenNavUI.xcframework */; };
766D8CBB2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 765A5A912F35CD8B0003FF7D /* PenNavUI.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 765A5A822F35CD730003FF7D /* PenNavUI.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
@ -41,15 +41,15 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = { 765A5A922F35CD8B0003FF7D /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
dstPath = ""; dstPath = "";
dstSubfolderSpec = 10; dstSubfolderSpec = 10;
files = ( files = (
766D8CB72EC60BE600D05E07 /* Penguin.xcframework in Embed Frameworks */, 765A5A8F2F35CD8B0003FF7D /* PenguinINRenderer.xcframework in Embed Frameworks */,
766D8CBB2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Embed Frameworks */, 765A5A8D2F35CD8B0003FF7D /* Penguin.xcframework in Embed Frameworks */,
766D8CB92EC60BE600D05E07 /* PenNavUI.xcframework in Embed Frameworks */, 765A5A912F35CD8B0003FF7D /* PenNavUI.xcframework in Embed Frameworks */,
); );
name = "Embed Frameworks"; name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -73,9 +73,9 @@
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7595037DD52211B91157B0F3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; 7595037DD52211B91157B0F3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
766D8CB32EC60BE600D05E07 /* Penguin.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Penguin.xcframework; path = Frameworks/Penguin.xcframework; sourceTree = "<group>"; }; 765A5A802F35CD730003FF7D /* Penguin.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Penguin.xcframework; sourceTree = "<group>"; };
766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PenNavUI.xcframework; path = Frameworks/PenNavUI.xcframework; sourceTree = "<group>"; }; 765A5A812F35CD730003FF7D /* PenguinINRenderer.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = PenguinINRenderer.xcframework; sourceTree = "<group>"; };
766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PenguinINRenderer.xcframework; path = Frameworks/PenguinINRenderer.xcframework; sourceTree = "<group>"; }; 765A5A822F35CD730003FF7D /* PenNavUI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = PenNavUI.xcframework; sourceTree = "<group>"; };
769C9BF82E6F106D009F68A9 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = "<group>"; }; 769C9BF82E6F106D009F68A9 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
8E12CEEB8E334EE22D5259D7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; 8E12CEEB8E334EE22D5259D7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
@ -95,9 +95,9 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
766D8CB62EC60BE600D05E07 /* Penguin.xcframework in Frameworks */, 765A5A8C2F35CD8B0003FF7D /* Penguin.xcframework in Frameworks */,
766D8CBA2EC60BE600D05E07 /* PenguinINRenderer.xcframework in Frameworks */, 765A5A902F35CD8B0003FF7D /* PenNavUI.xcframework in Frameworks */,
766D8CB82EC60BE600D05E07 /* PenNavUI.xcframework in Frameworks */, 765A5A8E2F35CD8B0003FF7D /* PenguinINRenderer.xcframework in Frameworks */,
DE1EF78253E64BE86845D2CC /* Pods_Runner.framework in Frameworks */, DE1EF78253E64BE86845D2CC /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -136,9 +136,9 @@
766D8CB22EC60BE600D05E07 /* Frameworks */ = { 766D8CB22EC60BE600D05E07 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
766D8CB32EC60BE600D05E07 /* Penguin.xcframework */, 765A5A802F35CD730003FF7D /* Penguin.xcframework */,
766D8CB52EC60BE600D05E07 /* PenguinINRenderer.xcframework */, 765A5A812F35CD730003FF7D /* PenguinINRenderer.xcframework */,
766D8CB42EC60BE600D05E07 /* PenNavUI.xcframework */, 765A5A822F35CD730003FF7D /* PenNavUI.xcframework */,
D562310E31D1DDEFA02A6C12 /* Pods_Runner.framework */, D562310E31D1DDEFA02A6C12 /* Pods_Runner.framework */,
); );
name = Frameworks; name = Frameworks;
@ -235,10 +235,10 @@
97C146EA1CF9000F007C117D /* Sources */, 97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */, 97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
8372B02399CDF54531650AD4 /* [CP] Embed Pods Frameworks */, 8372B02399CDF54531650AD4 /* [CP] Embed Pods Frameworks */,
81DE7C26F41956799E954FCE /* [CP] Copy Pods Resources */, 81DE7C26F41956799E954FCE /* [CP] Copy Pods Resources */,
765A5A922F35CD8B0003FF7D /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
); );
buildRules = ( buildRules = (
); );
@ -524,8 +524,8 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone"; PRODUCT_BUNDLE_IDENTIFIER = com.cloudsolutions.HMGPatientApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -710,8 +710,8 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone"; PRODUCT_BUNDLE_IDENTIFIER = com.cloudsolutions.HMGPatientApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -739,8 +739,8 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone"; PRODUCT_BUNDLE_IDENTIFIER = com.cloudsolutions.HMGPatientApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

@ -109,7 +109,7 @@
"size" : "83.5x83.5" "size" : "83.5x83.5"
}, },
{ {
"filename" : "icon 1.jpg", "filename" : "icon.jpg",
"idiom" : "ios-marketing", "idiom" : "ios-marketing",
"scale" : "1x", "scale" : "1x",
"size" : "1024x1024" "size" : "1024x1024"

@ -13,7 +13,7 @@
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>Dr. Alhabib</string> <string>Dr. Alhabib Beta</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>

@ -309,22 +309,13 @@ class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitia
PenNavUIManager.shared.eventsDelegate = self PenNavUIManager.shared.eventsDelegate = self
// print("====== after onPenNavSuccess navigate =========")
// PenNavUIManager.shared.navigate(to: "3-1")
print("====== after eventsDelegate onPenNavSuccess =========")
PenNavUIManager.shared.navigate(to: "108")
// Present the Penguin UI on top of the Flutter view controller // Present the Penguin UI on top of the Flutter view controller
print("====== after onPenNavSuccess present =========")
PenNavUIManager.shared.present(root: controller, view: _view) PenNavUIManager.shared.present(root: controller, view: _view)
print("====== after present onPenNavSuccess =========") print("====== after present onPenNavSuccess =========")
print(model?.clinicID) print(model?.clinicID)
@ -347,12 +338,22 @@ class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitia
let clientID = self.model?.clientID, !clientID.isEmpty else { let clientID = self.model?.clientID, !clientID.isEmpty else {
print("Error: Config Client ID is nil or empty") print("Error: Config Clinic ID is nil or empty")
return return
} }
print("====== after onPenNavSuccess navigate =========")
// PenNavUIManager.shared.navigate(to: "3-1") { success, error in
PenNavUIManager.shared.navigate(to: clinicID) { success, error in
if success {
print("Nav Success")
} else {
print("Nav Error")
}
}
let navigator = PenguinNavigator(config: config) let navigator = PenguinNavigator(config: config)
@ -373,20 +374,20 @@ class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitia
guard let token = token else { // guard let token = token else {
//
print("Token is nil") // print("Token is nil")
//
return // return
//
} // }
//
print("Token Generated") // print("Token Generated")
//
print(token); // print(token);
//
// self?.handleNavigation(clinicID: clinicID, token: token) { success, errorMessage in // self?.handleNavigation(clinicID: clinicID, token: token) { success, errorMessage in
// //
// if success { // if success {
@ -425,7 +426,8 @@ class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitia
PenNavUIManager.shared.setToken(token: token) PenNavUIManager.shared.setToken(token: token)
PenNavUIManager.shared.navigate(to: clinicID) // PenNavUIManager.shared.navigate(to: clinicID)
PenNavUIManager.shared.navigate(to: "3-1")
completion(true,nil) completion(true,nil)

@ -182,14 +182,15 @@ class ApiClientImp implements ApiClient {
} }
// body['TokenID'] = "@dm!n"; // body['TokenID'] = "@dm!n";
// body['PatientID'] = 1018977; // body['PatientID'] = 4768663;
// body['PatientTypeID'] = 1; // body['PatientTypeID'] = 1;
//
// body['PatientOutSA'] = 0; // body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q"; // body['SessionID'] = "45786230487560q";
} }
// body['IsNewFlutterApp'] = true; if(!url.contains("/paymentApi")) {
body['IsNewFlutterApp'] = true;
}
body.removeWhere((key, value) => value == null); body.removeWhere((key, value) => value == null);
} }
@ -208,10 +209,10 @@ class ApiClientImp implements ApiClient {
// Handle body encoding based on isBodyPlainText flag // Handle body encoding based on isBodyPlainText flag
final dynamic requestBody = isBodyPlainText ? body : json.encode(body); final dynamic requestBody = isBodyPlainText ? body : json.encode(body);
debugPrint("uri: ${Uri.parse(url.trim())}");
debugPrint("body: ${json.encode(body)}", wrapWidth: 2048);
final response = await http.post(Uri.parse(url.trim()), body: requestBody, headers: headers); final response = await http.post(Uri.parse(url.trim()), body: requestBody, headers: headers);
final int statusCode = response.statusCode; final int statusCode = response.statusCode;
log("uri: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
// log("response.body: ${response.body}"); // log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) { if (statusCode < 200 || statusCode >= 400) {
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data")); onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));

@ -83,6 +83,7 @@ var PRESCRIPTIONS = 'Services/Patients.svc/REST/GetPrescriptionApptList_Async';
var GET_PRESCRIPTIONS_ALL_ORDERS = 'Services/Patients.svc/REST/PatientER_GetPatientAllPresOrders'; var GET_PRESCRIPTIONS_ALL_ORDERS = 'Services/Patients.svc/REST/PatientER_GetPatientAllPresOrders';
var GET_PRESCRIPTION_REPORT = 'Services/Patients.svc/REST/INP_GetPrescriptionReport'; var GET_PRESCRIPTION_REPORT = 'Services/Patients.svc/REST/INP_GetPrescriptionReport';
var SEND_PRESCRIPTION_EMAIL = 'Services/Notifications.svc/REST/SendPrescriptionEmail'; var SEND_PRESCRIPTION_EMAIL = 'Services/Notifications.svc/REST/SendPrescriptionEmail';
var SEND_PRESCRIPTION_EMAIL_NEW = 'Services/Notifications.svc/REST/SendPrescriptionEmail_New';
var GET_PRESCRIPTION_REPORT_ENH = 'Services/Patients.svc/REST/GetPrescriptionReport_enh'; var GET_PRESCRIPTION_REPORT_ENH = 'Services/Patients.svc/REST/GetPrescriptionReport_enh';
///Lab Order ///Lab Order
@ -379,6 +380,8 @@ var GET_PATIENT_SICK_LEAVE = 'Services/Patients.svc/REST/GetPatientSickLeave';
var GET_PATIENT_SICK_LEAVE_STATUS = 'Services/Patients.svc/REST/GetPatientSickLeave_Status'; var GET_PATIENT_SICK_LEAVE_STATUS = 'Services/Patients.svc/REST/GetPatientSickLeave_Status';
var GET_SERVICES_PRICE_LIST = 'Services/OUTPs.svc/REST/GetServicesPriceList';
var SendSickLeaveEmail = 'Services/Notifications.svc/REST/SendSickLeaveEmail'; var SendSickLeaveEmail = 'Services/Notifications.svc/REST/SendSickLeaveEmail';
var GET_PATIENT_AdVANCE_BALANCE_AMOUNT = 'Services/Patients.svc/REST/GetPatientAdvanceBalanceAmount'; var GET_PATIENT_AdVANCE_BALANCE_AMOUNT = 'Services/Patients.svc/REST/GetPatientAdvanceBalanceAmount';
@ -680,7 +683,7 @@ const DASHBOARD = 'Services/Patients.svc/REST/PatientDashboard';
class ApiConsts { class ApiConsts {
static const maxSmallScreen = 660; static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@ -905,7 +908,7 @@ class ApiConsts {
static String getAiOverViewLabOrder = "Services/Patients.svc/REST/HMGAI_Lab_Analyzer_API"; static String getAiOverViewLabOrder = "Services/Patients.svc/REST/HMGAI_Lab_Analyzer_API";
// ************ static values for Api **************** // ************ static values for Api ****************
static final double appVersionID = 50.3; static final double appVersionID = 19.7;
static final int appChannelId = 3; static final int appChannelId = 3;
static final String appIpAddress = "10.20.10.20"; static final String appIpAddress = "10.20.10.20";
static final String appGeneralId = "Cs2020@2016\$2958"; static final String appGeneralId = "Cs2020@2016\$2958";

@ -54,6 +54,8 @@ import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_v
import 'package:hmg_patient_app_new/features/qr_parking/qr_parking_repo.dart'; import 'package:hmg_patient_app_new/features/qr_parking/qr_parking_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/features/services_price_list/services_price_list_repo.dart';
import 'package:hmg_patient_app_new/features/services_price_list/services_price_list_view_model.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart'; import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart';
import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_repo.dart'; import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_repo.dart';
import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart'; import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart';
@ -169,6 +171,7 @@ class AppDependencies {
getIt.registerLazySingleton<QrParkingRepo>(() => QrParkingRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<QrParkingRepo>(() => QrParkingRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<NotificationsRepo>(() => NotificationsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<NotificationsRepo>(() => NotificationsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<AskDoctorRepo>(() => AskDoctorRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<AskDoctorRepo>(() => AskDoctorRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<ServicesPriceListRepo>(() => ServicesPriceListRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
// ViewModels // ViewModels
// Global/shared VMs LazySingleton // Global/shared VMs LazySingleton
@ -311,5 +314,6 @@ class AppDependencies {
getIt.registerLazySingleton<AskDoctorViewModel>(() => AskDoctorViewModel(askDoctorRepo: getIt(), errorHandlerService: getIt())); getIt.registerLazySingleton<AskDoctorViewModel>(() => AskDoctorViewModel(askDoctorRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<ServicesPriceListViewModel>(() => ServicesPriceListViewModel(servicesPriceListRepo: getIt(), errorHandlerService: getIt()));
} }
} }

@ -110,7 +110,7 @@ class DateUtil {
} }
static String formatDateToTime(DateTime date) { static String formatDateToTime(DateTime date) {
return DateFormat('hh:mm a').format(date); return DateFormat('hh:mm a', "en-US").format(date);
} }
static String yearMonthDay(DateTime dateTime) { static String yearMonthDay(DateTime dateTime) {
@ -487,7 +487,9 @@ class DateUtil {
} }
static String getFormattedDate(DateTime dateTime, String formattedString) { static String getFormattedDate(DateTime dateTime, String formattedString) {
return DateFormat(formattedString).format(dateTime); String formattedDate = DateFormat(formattedString, "en-US").format(dateTime);
print(formattedDate);
return formattedDate;
} }
static convertISODateToJsonDate(String isoDate) { static convertISODateToJsonDate(String isoDate) {

@ -7,7 +7,7 @@ class PenguinMethodChannel {
return await rootBundle.load("assets/images/progress-loading-red-crop-1.gif").then((data) => data.buffer.asUint8List()); return await rootBundle.load("assets/images/progress-loading-red-crop-1.gif").then((data) => data.buffer.asUint8List());
} }
Future<void> launch(String storyboardName, String languageCode, String username, {NavigationClinicDetails? details}) async { Future<void> launch(String storyboardName, String languageCode, String username, bool isClinicNavigation, {NavigationClinicDetails? details}) async {
// Uint8List image = await loadGif(); // Uint8List image = await loadGif();
try { try {
await _channel.invokeMethod('launchPenguin', { await _channel.invokeMethod('launchPenguin', {
@ -31,7 +31,7 @@ class PenguinMethodChannel {
"isEnableReportIssue": true, "isEnableReportIssue": true,
"languageCode": languageCode, "languageCode": languageCode,
"mapBoxKey": "pk.eyJ1IjoicndhaWQiLCJhIjoiY2x5cGo4aHNjMGNsbTJyc2djempobGQxaSJ9.RCaC6WrUt4A4YnZNfxnONQ", "mapBoxKey": "pk.eyJ1IjoicndhaWQiLCJhIjoiY2x5cGo4aHNjMGNsbTJyc2djempobGQxaSJ9.RCaC6WrUt4A4YnZNfxnONQ",
"clinicID": details?.clinicId ?? "", "clinicID": isClinicNavigation ? "${details?.projectId}-${details?.clinicId}" : details?.clinicId ?? "",
// "clinicID": "108", // 46 ,49, 133 // "clinicID": "108", // 46 ,49, 133
"patientID": details?.patientId ?? "", "patientID": details?.patientId ?? "",
"projectID": int.parse(details?.projectId ?? "-1"), "projectID": int.parse(details?.projectId ?? "-1"),

@ -372,6 +372,14 @@ class PushNotificationHandler {
} }
}); });
if (Platform.isIOS) {
FirebaseMessaging.instance.getAPNSToken().then((String? token) {
print("Push Notification getAPNSToken: ${token!}");
}).catchError((err) {
print("Push Notification getAPNSToken ERR: ${err.toString()}");
});
}
FirebaseMessaging.instance.getToken().then((String? token) { FirebaseMessaging.instance.getToken().then((String? token) {
print("Push Notification getToken: ${token!}"); print("Push Notification getToken: ${token!}");
onToken(token!); onToken(token!);

@ -210,8 +210,8 @@ class RequestUtils {
List<String> names = fullName != null ? fullName.split(" ") : []; List<String> names = fullName != null ? fullName.split(" ") : [];
var dob = appState.getUserRegistrationPayload.dob; var dob = appState.getUserRegistrationPayload.dob;
final DateFormat dateFormat1 = DateFormat('MM/dd/yyyy'); final DateFormat dateFormat1 = DateFormat('MM/dd/yyyy', "en-US");
final DateFormat dateFormat2 = DateFormat('dd/MM/yyyy'); final DateFormat dateFormat2 = DateFormat('dd/MM/yyyy', "en-US");
DateTime gregorianDate = dateFormat2.parse(dob!); DateTime gregorianDate = dateFormat2.parse(dob!);
HijriGregDate hijriDate = HijriGregConverter.gregorianToHijri(gregorianDate); HijriGregDate hijriDate = HijriGregConverter.gregorianToHijri(gregorianDate);
String? date = "${hijriDate.day}/${hijriDate.month}/${hijriDate.year}"; String? date = "${hijriDate.day}/${hijriDate.month}/${hijriDate.year}";

@ -128,7 +128,7 @@ class Utils {
static String convertStringToDateTime(String dateTimeString) { static String convertStringToDateTime(String dateTimeString) {
String timeString = dateTimeString; String timeString = dateTimeString;
// Parse the time string using DateFormat // Parse the time string using DateFormat
DateFormat format = DateFormat.Hms(); // 'Hms' = 'HH:mm:ss' DateFormat format = DateFormat.Hms(["en-US"]); // 'Hms' = 'HH:mm:ss'
DateTime time = format.parse(timeString); DateTime time = format.parse(timeString);
DateTime now = DateTime.now(); DateTime now = DateTime.now();
@ -977,4 +977,27 @@ class Utils {
return checkDate == today; return checkDate == today;
} }
static bool isClinicAllowedForRebook(int clinicID) {
bool isAllowed = true;
if (clinicID == 23 || clinicID == 47 || clinicID == 265 || clinicID == 253 || clinicID == 134) {
isAllowed = false;
}
return isAllowed;
}
static String toEnglishNumbers(String input) {
const english = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
const persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
String result = input;
for (int i = 0; i < 10; i++) {
result = result.replaceAll(arabic[i], english[i]);
result = result.replaceAll(persian[i], english[i]);
}
return result;
}
} }

@ -23,8 +23,7 @@ extension CapExtension on String {
extension EmailValidator on String { extension EmailValidator on String {
Widget get toWidget => Text(this); Widget get toWidget => Text(this);
Widget toText8({Color? color, FontWeight? fontWeight, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Widget toText8({Color? color, FontWeight? fontWeight, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text(
Text(
this, this,
maxLines: maxlines, maxLines: maxlines,
overflow: textOverflow, overflow: textOverflow,
@ -38,7 +37,8 @@ extension EmailValidator on String {
); );
Widget toText10( Widget toText10(
{Color? color, {bool isEnglishOnly = false,
Color? color,
FontWeight? weight, FontWeight? weight,
bool isBold = false, bool isBold = false,
bool isUnderLine = false, bool isUnderLine = false,
@ -59,18 +59,36 @@ extension EmailValidator on String {
color: color ?? AppColors.blackColor, color: color ?? AppColors.blackColor,
letterSpacing: letterSpacing, letterSpacing: letterSpacing,
decoration: isUnderLine ? TextDecoration.underline : null, decoration: isUnderLine ? TextDecoration.underline : null,
fontFamily: isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins',
decorationColor: color ?? AppColors.blackColor), decorationColor: color ?? AppColors.blackColor),
); );
Widget toText11( Widget toText9(
{Color? color, {Color? color,
FontWeight? weight, FontWeight? weight,
bool isBold = false,
bool isUnderLine = false, bool isUnderLine = false,
bool isCenter = false, bool isCenter = false,
bool isBold = false, int? maxlines,
int maxLine = 0, FontStyle? fontStyle,
TextOverflow? textOverflow,
double letterSpacing = 0}) => double letterSpacing = 0}) =>
Text( Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: maxlines,
overflow: textOverflow,
style: TextStyle(
fontSize: 9.f,
fontStyle: fontStyle ?? FontStyle.normal,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor,
letterSpacing: letterSpacing,
decoration: isUnderLine ? TextDecoration.underline : null,
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(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
maxLines: (maxLine > 0) ? maxLine : null, maxLines: (maxLine > 0) ? maxLine : null,
@ -85,7 +103,7 @@ extension EmailValidator on String {
); );
Widget toText12( Widget toText12(
{Color? color, {bool isEnglishOnly = false, Color? color,
bool isUnderLine = false, bool isUnderLine = false,
TextAlign textAlignment = TextAlign.start, TextAlign textAlignment = TextAlign.start,
bool isBold = false, bool isBold = false,
@ -106,6 +124,7 @@ extension EmailValidator on String {
height: height, height: height,
decorationColor: isUnderLine ? AppColors.blackColor : null, decorationColor: isUnderLine ? AppColors.blackColor : null,
decoration: isUnderLine ? TextDecoration.underline : null, decoration: isUnderLine ? TextDecoration.underline : null,
fontFamily: isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins',
), ),
); );
@ -151,15 +170,7 @@ extension EmailValidator on String {
), ),
); );
Widget toText13( Widget toText13({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0, FontWeight? weight, double? letterSpacing = 0}) => Text(
{Color? color,
bool isUnderLine = false,
bool isBold = false,
bool isCenter = false,
int maxLine = 0,
FontWeight? weight,
double? letterSpacing = 0}) =>
Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
maxLines: (maxLine > 0) ? maxLine : null, maxLines: (maxLine > 0) ? maxLine : null,
@ -172,6 +183,7 @@ extension EmailValidator on String {
); );
Widget toText14({ Widget toText14({
bool isEnglishOnly = false,
Color? color, Color? color,
bool isUnderLine = false, bool isUnderLine = false,
bool isBold = false, bool isBold = false,
@ -194,18 +206,11 @@ extension EmailValidator on String {
height: height, height: height,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
decoration: isUnderLine ? TextDecoration.underline : null, decoration: isUnderLine ? TextDecoration.underline : null,
fontFamily: isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins',
decorationColor: color ?? AppColors.blackColor), decorationColor: color ?? AppColors.blackColor),
); );
Widget toText15( Widget toText15({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, FontWeight? weight, int? maxlines, double? letterSpacing = -1}) => Text(
{Color? color,
bool isUnderLine = false,
bool isBold = false,
bool isCenter = false,
FontWeight? weight,
int? maxlines,
double? letterSpacing = -1}) =>
Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
maxLines: maxlines, maxLines: maxlines,
@ -217,8 +222,7 @@ extension EmailValidator on String {
decoration: isUnderLine ? TextDecoration.underline : null), decoration: isUnderLine ? TextDecoration.underline : null),
); );
Widget toText16( Widget toText16({bool isEnglishOnly = false, Color? color,
{Color? color,
bool isUnderLine = false, bool isUnderLine = false,
bool isBold = false, bool isBold = false,
bool isCenter = false, bool isCenter = false,
@ -233,6 +237,7 @@ extension EmailValidator on String {
this, this,
maxLines: maxlines, maxLines: maxlines,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
// locale: Locale('en', 'US'),
style: TextStyle( style: TextStyle(
color: color ?? AppColors.blackColor, color: color ?? AppColors.blackColor,
fontSize: 16.f, fontSize: 16.f,
@ -241,14 +246,14 @@ extension EmailValidator on String {
overflow: textOverflow, overflow: textOverflow,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
decoration: isUnderLine ? TextDecoration.underline : null, decoration: isUnderLine ? TextDecoration.underline : null,
fontFamily: isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins',
decorationColor: decorationColor), decorationColor: decorationColor),
); );
Widget toText17({Color? color, bool isBold = false, bool isCenter = false}) => Text( Widget toText17({bool isEnglishOnly = false, Color? color, bool isBold = false, bool isCenter = false}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
style: TextStyle( style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 17.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),
color: color ?? AppColors.blackColor, fontSize: 17.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
); );
Widget toText18({Color? color, FontWeight? weight, bool isBold = false, bool isCenter = false, int? maxlines, TextOverflow? textOverflow}) => Text( Widget toText18({Color? color, FontWeight? weight, bool isBold = false, bool isCenter = false, int? maxlines, TextOverflow? textOverflow}) => Text(
@ -256,17 +261,12 @@ extension EmailValidator on String {
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
this, this,
overflow: textOverflow, overflow: textOverflow,
style: TextStyle( style: TextStyle(fontSize: 18.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4),
fontSize: 18.f,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor,
letterSpacing: -0.4),
); );
Widget toText19({Color? color, bool isBold = false}) => Text( Widget toText19({Color? color, bool isBold = false}) => Text(
this, this,
style: TextStyle( style: TextStyle(fontSize: 19.f, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
fontSize: 19.f, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
); );
Widget toText20({ Widget toText20({
@ -276,86 +276,51 @@ extension EmailValidator on String {
}) => }) =>
Text( Text(
this, this,
style: TextStyle( style: TextStyle(fontSize: 20.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4),
fontSize: 20.f,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor,
letterSpacing: -0.4),
); );
Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text( Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text(
this, this,
maxLines: maxlines, maxLines: maxlines,
style: TextStyle( style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 21.f, letterSpacing: -1, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
color: color ?? AppColors.blackColor,
fontSize: 21.f,
letterSpacing: -1,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
); );
Widget toText22({Color? color, bool isBold = false, bool isCenter = false}) => Text( Widget toText22({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
style: TextStyle( style: TextStyle(height: 1, color: color ?? AppColors.blackColor, fontSize: 22.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
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}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
style: TextStyle( style: TextStyle(
height: 23 / 24, height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.f, letterSpacing: letterSpacing ?? -1, fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.normal),
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}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
style: TextStyle( style: TextStyle(
height: height ?? 23 / 26, height: height ?? 23 / 26, color: color ?? AppColors.blackColor, fontSize: 26.f, letterSpacing: letterSpacing ?? -1, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
color: color ?? AppColors.blackColor,
fontSize: 26.f,
letterSpacing: letterSpacing ?? -1,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
); );
Widget toText28({Color? color, bool isBold = false, double? height, bool isCenter = false, double? letterSpacing}) => Text( Widget toText28({bool isEnglishOnly = false, Color? color, bool isBold = false, double? height, bool isCenter = false, double? letterSpacing}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
style: TextStyle( 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<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),
height: height ?? 23 / 28,
color: color ?? AppColors.blackColor,
fontSize: 28.f,
letterSpacing: letterSpacing ?? -1,
fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
); );
Widget toText32({FontWeight? weight, Color? color, bool isBold = false, bool isCenter = false}) => Text( Widget toText32({bool isEnglishOnly = false, FontWeight? weight, Color? color, bool isBold = false, bool isCenter = false}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
style: TextStyle( style: TextStyle(
height: 32 / 32, height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.f, letterSpacing: -1, fontFamily: isEnglishOnly ? "Poppins" : getIt.get<AppState>().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins', fontWeight: isBold ? FontWeight.bold : weight ?? FontWeight.normal),
color: color ?? AppColors.blackColor,
fontSize: 32.f,
letterSpacing: -1,
fontWeight: isBold ? FontWeight.bold : weight ?? FontWeight.normal),
); );
Widget toText44({Color? color, bool isBold = false}) => Text( Widget toText44({Color? color, bool isBold = false}) => Text(
this, this,
style: TextStyle( style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
height: 32 / 32,
color: color ?? AppColors.blackColor,
fontSize: 44.f,
letterSpacing: -1,
fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
); );
Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) { Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) {
@ -391,9 +356,7 @@ extension EmailValidator on String {
} }
bool isValidEmail() { bool isValidEmail() {
return RegExp( return RegExp(r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$').hasMatch(this);
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')
.hasMatch(this);
} }
String toFormattedDate() { String toFormattedDate() {

@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle; import 'package:flutter/services.dart' show rootBundle;
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart'; import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart';
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/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart'; import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
@ -74,6 +75,8 @@ class AuthenticationViewModel extends ChangeNotifier {
dobController = TextEditingController(), dobController = TextEditingController(),
nameController = TextEditingController(), nameController = TextEditingController(),
emailController = TextEditingController(); emailController = TextEditingController();
CountryEnum selectedCountrySignup = CountryEnum.saudiArabia; CountryEnum selectedCountrySignup = CountryEnum.saudiArabia;
MaritalStatusTypeEnum? maritalStatus; MaritalStatusTypeEnum? maritalStatus;
GenderTypeEnum? genderType; GenderTypeEnum? genderType;
@ -581,8 +584,8 @@ class AuthenticationViewModel extends ChangeNotifier {
if (!_appState.getIsChildLoggedIn) { if (!_appState.getIsChildLoggedIn) {
await medicalVm.getFamilyFiles(status: 0); await medicalVm.getFamilyFiles(status: 0);
// await medicalVm.getAllPendingRecordsByResponseId(); // await medicalVm.getAllPendingRecordsByResponseId();
// _navigationService.replaceAllRoutesAndNavigateToLanding();
_navigationService.popUntilNamed(AppRoutes.landingScreen); _navigationService.popUntilNamed(AppRoutes.landingScreen);
// _navigationService.replaceAllRoutesAndNavigateToLanding();
} }
} else { } else {
if (activation.list != null && activation.list!.isNotEmpty) { if (activation.list != null && activation.list!.isNotEmpty) {
@ -606,7 +609,7 @@ class AuthenticationViewModel extends ChangeNotifier {
} }
// _appState.setUserBloodGroup = (activation.patientBlodType ?? ""); // _appState.setUserBloodGroup = (activation.patientBlodType ?? "");
_appState.setAppAuthToken = activation.authenticationTokenId; _appState.setAppAuthToken = activation.authenticationTokenId;
myAppointmentsVM.getActiveAppointmentsCount(); // myAppointmentsVM.getActiveAppointmentsCount();
final request = RequestUtils.getAuthanticatedCommonRequest().toJson(); final request = RequestUtils.getAuthanticatedCommonRequest().toJson();
bool isUserAgreedBefore = await checkIfUserAgreedBefore(request: request); bool isUserAgreedBefore = await checkIfUserAgreedBefore(request: request);
@ -1045,6 +1048,7 @@ class AuthenticationViewModel extends ChangeNotifier {
} }
Future<void> getServicePrivilege() async { Future<void> getServicePrivilege() async {
ApiConsts.setBackendURLs();
final resultEither = await _authenticationRepo.getServicePrivilege(); final resultEither = await _authenticationRepo.getServicePrivilege();
List<ListPrivilege> privilegeModelList = []; List<ListPrivilege> privilegeModelList = [];
List<VidaPlusProjectListModel> vidaPlusProjectListModel = []; List<VidaPlusProjectListModel> vidaPlusProjectListModel = [];

@ -30,6 +30,8 @@ abstract class BookAppointmentsRepo {
Future<Either<Failure, GenericApiModel<DoctorsProfileResponseModel>>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}); Future<Either<Failure, GenericApiModel<DoctorsProfileResponseModel>>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorRatingDetails(int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare,
{bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}); {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError});
@ -183,7 +185,7 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
body: mapDevice, body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType; failure = failureType;
onError!(error); // onError!(error);
}, },
onSuccess: (response, statusCode, {messageStatus, errorMessage}) { onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try { try {
@ -298,6 +300,52 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
} }
} }
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorRatingDetails(int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"DoctorID": doctorId,
"PatientID": 0,
"License": true,
"IsRegistered": true,
"isDentalAllowedBackend": false,
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
GET_DOCTOR_RATING_DETAILS,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
if (onError != null) {
onError(error);
}
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response,
);
if (onSuccess != null) {
onSuccess(response);
}
} 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()));
}
}
//TODO: Implement the logic for Dental & laser clinics //TODO: Implement the logic for Dental & laser clinics
@override @override
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare,

@ -17,6 +17,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_rating_details_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart';
@ -92,6 +93,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
late DoctorsProfileResponseModel doctorsProfileResponseModel; late DoctorsProfileResponseModel doctorsProfileResponseModel;
bool isDoctorRatingDetailsLoading = false;
List<DoctorRateDetails> doctorDetailsList = [];
List<FreeSlot> slotsList = []; List<FreeSlot> slotsList = [];
List<TimeSlot> docFreeSlots = []; List<TimeSlot> docFreeSlots = [];
List<TimeSlot> dayEvents = []; List<TimeSlot> dayEvents = [];
@ -99,6 +103,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
String selectedAppointmentDate = ""; String selectedAppointmentDate = "";
String selectedAppointmentTime = ""; String selectedAppointmentTime = "";
String selectedAppointmentDisplayDate = "";
dynamic freeSlotsResponse; dynamic freeSlotsResponse;
@ -340,6 +345,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setIsLiveCareDoctorsListLoading(bool value) {
if (value) {
liveCareDoctorsList.clear();
}
isDoctorsListLoading = value;
notifyListeners();
}
setIsClinicsListLoading(bool value) { setIsClinicsListLoading(bool value) {
if (value) { if (value) {
clinicsList.clear(); clinicsList.clear();
@ -373,9 +386,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setSelectedAppointmentDateTime(String date, String time) { setSelectedAppointmentDateTime(String date, String time, String displayDate) {
selectedAppointmentDate = date; selectedAppointmentDate = date;
selectedAppointmentTime = time; selectedAppointmentTime = time;
selectedAppointmentDisplayDate = displayDate;
notifyListeners(); notifyListeners();
} }
@ -467,10 +481,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} }
Future<void> getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear(); liveCareDoctorsList.clear();
notifyListeners();
final result = final result =
await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError); await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError);
result.fold( result.fold(
(failure) async { (failure) async {
onError!(LocaleKeys.noDoctorFound.tr()); onError!(LocaleKeys.noDoctorFound.tr());
@ -569,7 +583,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
result.fold( result.fold(
(failure) async { (failure) async {
onError?.call(LocaleKeys.noDoctorFound.tr()); onError?.call(selectedClinic.clinicID == 23 ? failure.message : LocaleKeys.noDoctorFound.tr());
}, },
(apiResponse) async { (apiResponse) async {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
@ -614,6 +628,46 @@ class BookAppointmentsViewModel extends ChangeNotifier {
); );
} }
Future<void> getDoctorRatingDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
isDoctorRatingDetailsLoading = true;
doctorDetailsList.clear();
notifyListeners();
final result = await bookAppointmentsRepo.getDoctorRatingDetails(selectedDoctor.doctorID ?? 0, onSuccess: onSuccess, onError: onError);
result.fold(
(failure) async {
if (onError != null) {
onError("Failed to load doctor rating details");
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
if (onError != null) {
onError(apiResponse.errorMessage ?? "Unknown error occurred");
}
isDoctorRatingDetailsLoading = false;
} else if (apiResponse.messageStatus == 1) {
try {
apiResponse.data['DoctorRatingDetailsList'].forEach((v) {
doctorDetailsList.add(DoctorRateDetails.fromJson(v));
});
isDoctorRatingDetailsLoading = false;
// doctorRatingDetails = DoctorRateDetails.fromJson(apiResponse.data);
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse.data);
}
} catch (e) {
if (onError != null) {
onError("Failed to parse rating details: ${e.toString()}");
}
}
}
},
);
}
Future<void> getDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
docFreeSlots.clear(); docFreeSlots.clear();
DateTime date; DateTime date;
@ -669,7 +723,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future<void> getLiveCareDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getLiveCareDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
docFreeSlots.clear(); docFreeSlots.clear();
DateTime date; DateTime date;
final DateFormat formatter = DateFormat('HH:mm'); final DateFormat formatter = DateFormat('HH:mm', "en-US");
final DateFormat dateFormatter = DateFormat('yyyy-MM-dd'); final DateFormat dateFormatter = DateFormat('yyyy-MM-dd');
Map<DateTime, List> _eventsParsed; Map<DateTime, List> _eventsParsed;
@ -754,6 +808,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
result.fold( result.fold(
(failure) async { (failure) async {
print(failure); print(failure);
onError!(failure.message);
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {

@ -0,0 +1,36 @@
class DoctorRateDetails {
dynamic doctorID;
dynamic projectID;
dynamic clinicID;
dynamic rate;
dynamic patientNumber;
dynamic ratio;
DoctorRateDetails(
{this.doctorID,
this.projectID,
this.clinicID,
this.rate,
this.patientNumber,
this.ratio});
DoctorRateDetails.fromJson(Map<String, dynamic> json) {
doctorID = json['DoctorID'];
projectID = json['ProjectID'];
clinicID = json['ClinicID'];
rate = json['Rate'];
patientNumber = json['PatientNumber'];
ratio = json['Ratio'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['DoctorID'] = this.doctorID;
data['ProjectID'] = this.projectID;
data['ClinicID'] = this.clinicID;
data['Rate'] = this.rate;
data['PatientNumber'] = this.patientNumber;
data['Ratio'] = this.ratio;
return data;
}
}

@ -9,6 +9,7 @@ class HabibWalletViewModel extends ChangeNotifier {
bool isWalletAmountLoading = false; bool isWalletAmountLoading = false;
num habibWalletAmount = 0; num habibWalletAmount = 0;
num walletRechargeAmount = 0; num walletRechargeAmount = 0;
String notesText = "";
bool isBottomSheetContentLoading = false; bool isBottomSheetContentLoading = false;
@ -60,6 +61,11 @@ class HabibWalletViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setNotesText(String notes) {
notesText = notes;
notifyListeners();
}
setDepositorDetails(String fileNum, String depositor, String mobile) { setDepositorDetails(String fileNum, String depositor, String mobile) {
fileNumber = fileNum; fileNumber = fileNum;
depositorName = depositor; depositorName = depositor;

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.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/enums.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart';
@ -17,6 +18,7 @@ import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/vital_sign_respo_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/vital_sign_respo_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
@ -31,8 +33,7 @@ class HmgServicesViewModel extends ChangeNotifier {
final ErrorHandlerService errorHandlerService; final ErrorHandlerService errorHandlerService;
final NavigationService navigationService; final NavigationService navigationService;
HmgServicesViewModel( HmgServicesViewModel({required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService, required this.navigationService});
{required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService, required this.navigationService});
bool isCmcOrdersLoading = false; bool isCmcOrdersLoading = false;
bool isCmcServicesLoading = false; bool isCmcServicesLoading = false;
@ -55,9 +56,11 @@ class HmgServicesViewModel extends ChangeNotifier {
// Vital Sign PageView Controller // Vital Sign PageView Controller
PageController _vitalSignPageController = PageController(); PageController _vitalSignPageController = PageController();
PageController get vitalSignPageController => _vitalSignPageController; PageController get vitalSignPageController => _vitalSignPageController;
int _vitalSignCurrentPage = 0; int _vitalSignCurrentPage = 0;
int get vitalSignCurrentPage => _vitalSignCurrentPage; int get vitalSignCurrentPage => _vitalSignCurrentPage;
void setVitalSignCurrentPage(int page) { void setVitalSignCurrentPage(int page) {
@ -65,7 +68,6 @@ class HmgServicesViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
// HHC specific lists // HHC specific lists
List<GetCMCAllOrdersResponseModel> hhcOrdersList = []; List<GetCMCAllOrdersResponseModel> hhcOrdersList = [];
List<GetCMCServicesResponseModel> hhcServicesList = []; List<GetCMCServicesResponseModel> hhcServicesList = [];
@ -82,9 +84,8 @@ class HmgServicesViewModel extends ChangeNotifier {
List<SearchEReferralResponseModel> searchReferralList = []; List<SearchEReferralResponseModel> searchReferralList = [];
List<Covid19GetTestProceduresResp> covidTestProcedureList = []; List<Covid19GetTestProceduresResp> covidTestProcedureList = [];
Covid19GetPaymentInfo? covidPaymentInfo; Covid19GetPaymentInfo? covidPaymentInfo;
Future<void> getOrdersList() async {}
Future<void> getOrdersList() async {}
// HHC multiple services selection // HHC multiple services selection
List<GetCMCServicesResponseModel> selectedHhcServices = []; List<GetCMCServicesResponseModel> selectedHhcServices = [];
@ -792,22 +793,20 @@ class HmgServicesViewModel extends ChangeNotifier {
}, },
); );
} }
List<CovidQuestionnaireModel> getQuestionsFromJson() { List<CovidQuestionnaireModel> getQuestionsFromJson() {
final String questionsJson = '''[ { "id": 1, "questionEN": "Is the test intended for travel?", "questionAR": "هل تجري التحليل بغرض السفر؟", "ans": 2 }, { "id": 2, "questionEN": "Coming from outside KSA within last 2 weeks?", "questionAR": "هل قدمت من خارج المملكة خلال الأسبوعين الماضيين؟", "ans": 2 }, { "id": 3, "questionEN": "Do you currently have fever?", "questionAR": "هل تعاني حاليا من حرارة؟", "ans": 2 }, { "id": 4, "questionEN": "Did you have fever in last 2 weeks?", "questionAR": "هل عانيت من حرارة في الأسبوعين الماضيين؟", "ans": 2 }, { "id": 5, "questionEN": "Do you have a sore throat?", "questionAR": "هل لديك التهاب في الحلق؟", "ans": 2 }, { "id": 6, "questionEN": "Do you have a runny nose?", "questionAR": "هل لديك سيلان بالأنف؟" }, { "id": 7, "questionEN": "Do you have a cough?", "questionAR": "هل لديك سعال؟", "ans": 2 }, { "id": 8, "questionEN": "Do you have shortness of breath?", "questionAR": "هل تعاني من ضيق في التنفس؟", "ans": 2 }, { "id": 9, "questionEN": "Do you have nausea?", "questionAR": "هل تعاني من غثيان؟", "ans": 2 }, { "id": 10, "questionEN": "Do you have vomiting?", "questionAR": "هل تعاني من القيء؟", "ans": 2 }, { "id": 11, "questionEN": "Do you have a headache?", "questionAR": "هل تعاني من صداع في الرأس؟", "ans": 2 }, { "id": 12, "questionEN": "Do you have muscle pain?", "questionAR": "هل تعانين من آلام عضلية؟", "ans": 2 }, { "id": 13, "questionEN": "Do you have joint pain?", "questionAR": "هل تعاني من آلام المفاصل؟", "ans": 2 }, { "id": 14, "questionEN": "Do you have diarrhea?", "questionAR": "هل لديك اسهال؟", "ans": 2 } ]'''; final String questionsJson =
'''[ { "id": 1, "questionEN": "Is the test intended for travel?", "questionAR": "هل تجري التحليل بغرض السفر؟", "ans": 2 }, { "id": 2, "questionEN": "Coming from outside KSA within last 2 weeks?", "questionAR": "هل قدمت من خارج المملكة خلال الأسبوعين الماضيين؟", "ans": 2 }, { "id": 3, "questionEN": "Do you currently have fever?", "questionAR": "هل تعاني حاليا من حرارة؟", "ans": 2 }, { "id": 4, "questionEN": "Did you have fever in last 2 weeks?", "questionAR": "هل عانيت من حرارة في الأسبوعين الماضيين؟", "ans": 2 }, { "id": 5, "questionEN": "Do you have a sore throat?", "questionAR": "هل لديك التهاب في الحلق؟", "ans": 2 }, { "id": 6, "questionEN": "Do you have a runny nose?", "questionAR": "هل لديك سيلان بالأنف؟" }, { "id": 7, "questionEN": "Do you have a cough?", "questionAR": "هل لديك سعال؟", "ans": 2 }, { "id": 8, "questionEN": "Do you have shortness of breath?", "questionAR": "هل تعاني من ضيق في التنفس؟", "ans": 2 }, { "id": 9, "questionEN": "Do you have nausea?", "questionAR": "هل تعاني من غثيان؟", "ans": 2 }, { "id": 10, "questionEN": "Do you have vomiting?", "questionAR": "هل تعاني من القيء؟", "ans": 2 }, { "id": 11, "questionEN": "Do you have a headache?", "questionAR": "هل تعاني من صداع في الرأس؟", "ans": 2 }, { "id": 12, "questionEN": "Do you have muscle pain?", "questionAR": "هل تعانين من آلام عضلية؟", "ans": 2 }, { "id": 13, "questionEN": "Do you have joint pain?", "questionAR": "هل تعاني من آلام المفاصل؟", "ans": 2 }, { "id": 14, "questionEN": "Do you have diarrhea?", "questionAR": "هل لديك اسهال؟", "ans": 2 } ]''';
try { try {
final parsed = json.decode(questionsJson) as List<dynamic>; final parsed = json.decode(questionsJson) as List<dynamic>;
return parsed return parsed.map((e) => CovidQuestionnaireModel.fromJson(Map<String, dynamic>.from(e))).toList();
.map((e) => CovidQuestionnaireModel.fromJson(Map<String, dynamic>.from(e)))
.toList();
} catch (_) { } catch (_) {
return <CovidQuestionnaireModel>[]; return <CovidQuestionnaireModel>[];
} }
} }
Future<void> getCovidProcedureList({ Future<void> getCovidProcedureList({
Function(dynamic)? onSuccess, Function(dynamic)? onSuccess,
Function(String)? onError, Function(String)? onError,
}) async { }) async {
@ -816,14 +815,14 @@ class HmgServicesViewModel extends ChangeNotifier {
final result = await hmgServicesRepo.getCovidTestProcedures(); final result = await hmgServicesRepo.getCovidTestProcedures();
result.fold( result.fold(
(failure) async { (failure) async {
notifyListeners(); notifyListeners();
await errorHandlerService.handleError(failure: failure); await errorHandlerService.handleError(failure: failure);
if (onError != null) { if (onError != null) {
onError(failure.toString()); onError(failure.toString());
} }
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 1) { if (apiResponse.messageStatus == 1) {
covidTestProcedureList = apiResponse.data ?? []; covidTestProcedureList = apiResponse.data ?? [];
notifyListeners(); notifyListeners();
@ -840,7 +839,6 @@ class HmgServicesViewModel extends ChangeNotifier {
); );
} }
Future<void> getPaymentInfo({ Future<void> getPaymentInfo({
String? procedureID, String? procedureID,
int? projectID, int? projectID,
@ -852,14 +850,14 @@ class HmgServicesViewModel extends ChangeNotifier {
final result = await hmgServicesRepo.getCovidPaymentInfo(procedureID!, projectID!); final result = await hmgServicesRepo.getCovidPaymentInfo(procedureID!, projectID!);
result.fold( result.fold(
(failure) async { (failure) async {
notifyListeners(); notifyListeners();
await errorHandlerService.handleError(failure: failure); await errorHandlerService.handleError(failure: failure);
if (onError != null) { if (onError != null) {
onError(failure.toString()); onError(failure.toString());
} }
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 1) { if (apiResponse.messageStatus == 1) {
covidPaymentInfo = apiResponse.data; covidPaymentInfo = apiResponse.data;
notifyListeners(); notifyListeners();
@ -900,6 +898,8 @@ class HmgServicesViewModel extends ChangeNotifier {
if (apiResponse.messageStatus == 1) { if (apiResponse.messageStatus == 1) {
vitalSignList = apiResponse.data ?? []; vitalSignList = apiResponse.data ?? [];
hasVitalSignDataLoaded = true; hasVitalSignDataLoaded = true;
getIt.get<SymptomsCheckerViewModel>().setSelectedHeight(vitalSignList.first.heightCm);
getIt.get<SymptomsCheckerViewModel>().setSelectedWeight(vitalSignList.first.weightKg);
notifyListeners(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);

@ -101,6 +101,7 @@ class HospitalSelectionBottomSheetViewModel extends ChangeNotifier {
NavigationClinicDetails data = NavigationClinicDetails(); NavigationClinicDetails data = NavigationClinicDetails();
data.projectId = projectID.toString(); data.projectId = projectID.toString();
data.clinicId = clinicID; data.clinicId = clinicID;
// data.clinicId = "1";
final bool permited = await AppPermission.askPenguinPermissions(); final bool permited = await AppPermission.askPenguinPermissions();
if (!permited) { if (!permited) {
Map<Permission, PermissionStatus> statuses = await [ Map<Permission, PermissionStatus> statuses = await [
@ -110,7 +111,7 @@ class HospitalSelectionBottomSheetViewModel extends ChangeNotifier {
Permission.bluetoothScan, Permission.bluetoothScan,
Permission.activityRecognition, Permission.activityRecognition,
].request().whenComplete(() { ].request().whenComplete(() {
PenguinMethodChannel().launch("penguin", appState.isArabic() ? "ar" : "en", appState.getAuthenticatedUser()?.patientId?.toString()??"", details: data); PenguinMethodChannel().launch("penguin", appState.isArabic() ? "ar" : "en", appState.getAuthenticatedUser()?.patientId?.toString() ?? "", false, details: data);
}); });
} }
} }

@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patien
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_update_response_model.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_update_response_model.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/upload_insurance_card_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class InsuranceRepo { abstract class InsuranceRepo {
@ -17,6 +18,14 @@ abstract class InsuranceRepo {
Future<Either<Failure, GenericApiModel<PatientInsuranceUpdateResponseModel>>> getPatientInsuranceDetailsForUpdate({required String patientId, required String identificationNo}); Future<Either<Failure, GenericApiModel<PatientInsuranceUpdateResponseModel>>> getPatientInsuranceDetailsForUpdate({required String patientId, required String identificationNo});
Future<Either<Failure, GenericApiModel<List<InsuranceApprovalResponseModel>>>> getPatientInsuranceApprovalsList(); Future<Either<Failure, GenericApiModel<List<InsuranceApprovalResponseModel>>>> getPatientInsuranceApprovalsList();
Future<Either<Failure, GenericApiModel<dynamic>>> updatePatientInsuranceCard({
required int patientID,
required int patientType,
required String mobileNo,
required String patientIdentificationID,
required String insuranceCardImage,
});
} }
class InsuranceRepoImp implements InsuranceRepo { class InsuranceRepoImp implements InsuranceRepo {
@ -183,4 +192,52 @@ class InsuranceRepoImp implements InsuranceRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel<dynamic>>> updatePatientInsuranceCard({
required int patientID,
required int patientType,
required String mobileNo,
required String patientIdentificationID,
required String insuranceCardImage,
}) async {
Map<String, dynamic> mapDevice = {
"PatientID": patientID,
"PatientType": patientType,
"MobileNo": mobileNo,
"PatientIdentificationID": patientIdentificationID,
"InsuranceCardImage": insuranceCardImage,
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
UPLOAD_INSURANCE_CARD,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
// final uploadResponse = UploadInsuranceCardResponseModel.fromJson(response);
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: apiResponse,
);
} 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()));
}
}
} }

@ -5,6 +5,7 @@ import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patien
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_update_response_model.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_update_response_model.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/upload_insurance_card_response_model.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class InsuranceViewModel extends ChangeNotifier { class InsuranceViewModel extends ChangeNotifier {
@ -171,4 +172,44 @@ class InsuranceViewModel extends ChangeNotifier {
}, },
); );
} }
Future<void> updatePatientInsuranceCard({
required int patientID,
required int patientType,
required String mobileNo,
required String patientIdentificationID,
required String insuranceCardImage,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await insuranceRepo.updatePatientInsuranceCard(
patientID: patientID,
patientType: patientType,
mobileNo: mobileNo,
patientIdentificationID: patientIdentificationID,
insuranceCardImage: insuranceCardImage,
);
result.fold(
(failure) async {
notifyListeners();
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? "Error updating insurance card");
}
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
} }

@ -231,6 +231,9 @@ class LabViewModel extends ChangeNotifier {
model: item)) model: item))
}; };
uniqueTestsList = uniqueTests.toList(); uniqueTestsList = uniqueTests.toList();
uniqueTestsList = Utils.uniqueBy(uniqueTestsList, (p) => p.testCode);
uniqueTestsList.sort((a, b) { uniqueTestsList.sort((a, b) {
return a.description!.toLowerCase().compareTo(b.description!.toLowerCase()); return a.description!.toLowerCase().compareTo(b.description!.toLowerCase());
}); });

@ -41,6 +41,7 @@ class MedicalFileViewModel extends ChangeNotifier {
List<SickLeaveList> patientSickLeavesViewList = []; List<SickLeaveList> patientSickLeavesViewList = [];
bool isSickLeavesSortByClinic = true; bool isSickLeavesSortByClinic = true;
bool isSickLeavesDataNeedsReloading = true;
List<GetAllergiesResponseModel> patientAllergiesList = []; List<GetAllergiesResponseModel> patientAllergiesList = [];
@ -163,15 +164,15 @@ class MedicalFileViewModel extends ChangeNotifier {
} }
setIsPatientSickLeaveListLoading(bool val) { setIsPatientSickLeaveListLoading(bool val) {
if (val) { if (val && isSickLeavesDataNeedsReloading) {
patientSickLeaveList.clear(); patientSickLeaveList.clear();
patientSickLeavesByClinic.clear(); patientSickLeavesByClinic.clear();
patientSickLeavesByHospital.clear(); patientSickLeavesByHospital.clear();
patientSickLeavesViewList.clear(); patientSickLeavesViewList.clear();
patientSickLeavePDFBase64 = ""; patientSickLeavePDFBase64 = "";
isSickLeavesSortByClinic = true; isSickLeavesSortByClinic = true;
isPatientSickLeaveListLoading = val;
} }
isPatientSickLeaveListLoading = val;
notifyListeners(); notifyListeners();
} }
@ -267,6 +268,10 @@ class MedicalFileViewModel extends ChangeNotifier {
} }
Future<void> getPatientSickLeaveList({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientSickLeaveList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isSickLeavesDataNeedsReloading) {
return;
}
patientSickLeaveList.clear(); patientSickLeaveList.clear();
final result = await medicalFileRepo.getPatientSickLeavesList(); final result = await medicalFileRepo.getPatientSickLeavesList();
@ -309,6 +314,7 @@ class MedicalFileViewModel extends ChangeNotifier {
} }
} }
patientSickLeavesViewList = patientSickLeavesByClinic; patientSickLeavesViewList = patientSickLeavesByClinic;
isSickLeavesDataNeedsReloading = false;
notifyListeners(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {

@ -5,14 +5,14 @@ class PatientAppointmentShareResponseModel {
int? appointmentNo; int? appointmentNo;
dynamic cardNumber; dynamic cardNumber;
dynamic cardType; dynamic cardType;
int? cashPrice; num? cashPrice;
int? cashPriceTax; num? cashPriceTax;
int? cashPriceWithTax; num? cashPriceWithTax;
int? clinicID; int? clinicID;
String? clinicName; String? clinicName;
int? companyId; int? companyId;
String? companyName; String? companyName;
int? companyShareWithTax; num? companyShareWithTax;
int? doctorID; int? doctorID;
String? doctorImageURL; String? doctorImageURL;
String? doctorNameObj; String? doctorNameObj;
@ -39,7 +39,7 @@ class PatientAppointmentShareResponseModel {
int? patientStatusType; int? patientStatusType;
num? patientTaxAmount; num? patientTaxAmount;
String? patientType; String? patientType;
int? paymentAmount; num? paymentAmount;
String? paymentDate; String? paymentDate;
dynamic paymentMethodName; dynamic paymentMethodName;
dynamic paymentReferenceNumber; dynamic paymentReferenceNumber;
@ -57,7 +57,7 @@ class PatientAppointmentShareResponseModel {
int? statusCode; int? statusCode;
dynamic statusDesc; dynamic statusDesc;
String? subPolicyNo; String? subPolicyNo;
int? tax; num? tax;
int? userID; int? userID;
PatientAppointmentShareResponseModel({ PatientAppointmentShareResponseModel({

@ -220,9 +220,23 @@ class MyAppointmentsViewModel extends ChangeNotifier {
} }
Future<void> getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
isPatientHasQueueAppointment = false; if (patientArrivedAppointmentsHistoryList.isNotEmpty) {
isPatientHasQueueAppointment = false;
notifyListeners();
if (Utils.isDateToday(DateUtil.convertStringToDate(patientArrivedAppointmentsHistoryList.first.appointmentDate))) {
// getPatientAppointmentQueueDetails(appointmentNo: patientArrivedAppointmentsHistoryList.first.appointmentNo, patientID: patientArrivedAppointmentsHistoryList.first.patientID);
getPatientAppointmentQueueDetails();
}
}
if (!isAppointmentDataToBeLoaded) return; if (!isAppointmentDataToBeLoaded) return;
debugPrint('getPatientAppointments CALLED!!!');
patientAppointmentsByClinic.clear();
patientAppointmentsByHospital.clear();
patientAppointmentsViewList.clear();
patientTimelineAppointmentsList.clear();
filteredAppointmentList.clear(); filteredAppointmentList.clear();
patientAppointmentsHistoryList.clear(); patientAppointmentsHistoryList.clear();
patientUpcomingAppointmentsHistoryList.clear(); patientUpcomingAppointmentsHistoryList.clear();
@ -276,6 +290,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
_groupAppointmentsByClinicAndHospital(); _groupAppointmentsByClinicAndHospital();
if (patientArrivedAppointmentsHistoryList.isNotEmpty) { if (patientArrivedAppointmentsHistoryList.isNotEmpty) {
isPatientHasQueueAppointment = false;
notifyListeners();
if (Utils.isDateToday(DateUtil.convertStringToDate(patientArrivedAppointmentsHistoryList.first.appointmentDate))) { if (Utils.isDateToday(DateUtil.convertStringToDate(patientArrivedAppointmentsHistoryList.first.appointmentDate))) {
// getPatientAppointmentQueueDetails(appointmentNo: patientArrivedAppointmentsHistoryList.first.appointmentNo, patientID: patientArrivedAppointmentsHistoryList.first.patientID); // getPatientAppointmentQueueDetails(appointmentNo: patientArrivedAppointmentsHistoryList.first.appointmentNo, patientID: patientArrivedAppointmentsHistoryList.first.patientID);
getPatientAppointmentQueueDetails(); getPatientAppointmentQueueDetails();
@ -494,7 +510,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
} }
Future<void> getPatientMyDoctors({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientMyDoctors({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isAppointmentDataToBeLoaded) return; // if (!isAppointmentDataToBeLoaded) return;
isPatientMyDoctorsLoading = true; isPatientMyDoctorsLoading = true;
notifyListeners(); notifyListeners();

@ -167,29 +167,44 @@ class PrescriptionsRepoImp implements PrescriptionsRepo {
@override @override
Future<Either<Failure, GenericApiModel>> getPrescriptionPDF( Future<Either<Failure, GenericApiModel>> getPrescriptionPDF(
{required PatientPrescriptionsResponseModel prescriptionsResponseModel, required List<PrescriptionDetailResponseModel> prescriptionDetailsList}) async { {required PatientPrescriptionsResponseModel prescriptionsResponseModel, required List<PrescriptionDetailResponseModel> prescriptionDetailsList}) async {
// Map<String, dynamic> mapDevice = {
// "AppointmentDate": prescriptionsResponseModel.appointmentDate,
// "ClinicName": prescriptionsResponseModel.clinicDescription,
// "DoctorName": prescriptionsResponseModel.doctorName,
// "ProjectID": prescriptionsResponseModel.projectID,
// "DoctorID": prescriptionsResponseModel.doctorID,
// "ClinicID": prescriptionsResponseModel.clinicID,
// "DateofBirth": Utils.appState.getAuthenticatedUser()!.dateofBirth,
// "ListPrescriptions": prescriptionDetailsList,
// "PatientIditificationNum": Utils.appState.getAuthenticatedUser()!.patientIdentificationNo,
// "PatientMobileNumber": Utils.appState.getAuthenticatedUser()!.mobileNumber,
// "PatientName": "${Utils.appState.getAuthenticatedUser()!.firstName!} ${Utils.appState.getAuthenticatedUser()!.lastName!}",
// "To": Utils.appState.getAuthenticatedUser()!.emailAddress,
// "SetupID": prescriptionsResponseModel.setupID,
// "IsDownload": true,
// "isDentalAllowedBackend": false,
// };
Map<String, dynamic> mapDevice = { Map<String, dynamic> mapDevice = {
"AppointmentDate": prescriptionsResponseModel.appointmentDate, "AppointmentNo": prescriptionsResponseModel.appointmentNo,
"ClinicName": prescriptionsResponseModel.clinicDescription,
"DoctorName": prescriptionsResponseModel.doctorName,
"ProjectID": prescriptionsResponseModel.projectID,
"DoctorID": prescriptionsResponseModel.doctorID,
"ClinicID": prescriptionsResponseModel.clinicID,
"DateofBirth": Utils.appState.getAuthenticatedUser()!.dateofBirth,
"ListPrescriptions": prescriptionDetailsList,
"PatientIditificationNum": Utils.appState.getAuthenticatedUser()!.patientIdentificationNo,
"PatientMobileNumber": Utils.appState.getAuthenticatedUser()!.mobileNumber,
"PatientName": "${Utils.appState.getAuthenticatedUser()!.firstName!} ${Utils.appState.getAuthenticatedUser()!.lastName!}",
"To": Utils.appState.getAuthenticatedUser()!.emailAddress,
"SetupID": prescriptionsResponseModel.setupID, "SetupID": prescriptionsResponseModel.setupID,
"EpisodeID": prescriptionsResponseModel.episodeID,
"ClinicID": prescriptionsResponseModel.clinicID,
"ProjectID": prescriptionsResponseModel.projectID,
"DischargeNo": prescriptionsResponseModel.dischargeNo,
"IsDownload": true, "IsDownload": true,
"isDentalAllowedBackend": false, // "To": Utils.appState.getAuthenticatedUser()!.emailAddress,
"To": "amjad.haroon@cloudsolutions.com.sa",
"ClinicName": prescriptionsResponseModel.clinicDescription,
"DoctorName": prescriptionsResponseModel.doctorName,
}; };
try { try {
GenericApiModel<dynamic>? apiResponse; GenericApiModel<dynamic>? apiResponse;
Failure? failure; Failure? failure;
await apiClient.post( await apiClient.post(
SEND_PRESCRIPTION_EMAIL, // SEND_PRESCRIPTION_EMAIL,
SEND_PRESCRIPTION_EMAIL_NEW,
body: mapDevice, body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType; failure = failureType;

@ -47,20 +47,24 @@ class PrescriptionsViewModel extends ChangeNotifier {
late GeocodeResponse locationGeocodeResponse; late GeocodeResponse locationGeocodeResponse;
bool isPrescriptionsDeliveryOrdersLoading = false; bool isPrescriptionsDeliveryOrdersLoading = false;
bool isPrescriptionsDataNeedsReloading = true;
List<PrescriptionDeliveryResponseModel> prescriptionsOrderList = []; List<PrescriptionDeliveryResponseModel> prescriptionsOrderList = [];
PrescriptionsViewModel({required this.prescriptionsRepo, required this.errorHandlerService, required this.navServices}); PrescriptionsViewModel({required this.prescriptionsRepo, required this.errorHandlerService, required this.navServices});
initPrescriptionsViewModel() { initPrescriptionsViewModel() {
patientPrescriptionOrders.clear(); if (isPrescriptionsDataNeedsReloading) {
patientPrescriptionOrdersByClinic.clear(); patientPrescriptionOrders.clear();
patientPrescriptionOrdersByHospital.clear(); patientPrescriptionOrdersByClinic.clear();
patientPrescriptionOrdersViewList.clear(); patientPrescriptionOrdersByHospital.clear();
prescriptionsOrderList.clear(); patientPrescriptionOrdersViewList.clear();
isPrescriptionsOrdersLoading = true; prescriptionsOrderList.clear();
isPrescriptionsOrdersLoading = true;
getPatientPrescriptionOrders();
}
isSortByClinic = true; isSortByClinic = true;
isPrescriptionsDeliveryOrdersLoading = true; isPrescriptionsDeliveryOrdersLoading = true;
getPatientPrescriptionOrders();
notifyListeners(); notifyListeners();
} }
@ -98,6 +102,10 @@ class PrescriptionsViewModel extends ChangeNotifier {
} }
Future<void> getPatientPrescriptionOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientPrescriptionOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isPrescriptionsDataNeedsReloading) {
return;
}
final result = await prescriptionsRepo.getPatientPrescriptionOrders(patientId: "1231755"); final result = await prescriptionsRepo.getPatientPrescriptionOrders(patientId: "1231755");
result.fold( result.fold(
@ -131,6 +139,7 @@ class PrescriptionsViewModel extends ChangeNotifier {
} }
} }
patientPrescriptionOrdersViewList = patientPrescriptionOrdersByClinic; patientPrescriptionOrdersViewList = patientPrescriptionOrdersByClinic;
isPrescriptionsDataNeedsReloading = false;
notifyListeners(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);

@ -0,0 +1,54 @@
class ServicesPriceListResponseModel {
int? createdBy;
String? createdOn;
int? editedBy;
String? editedOn;
int? id;
bool? isEnabled;
String? nameAR;
String? nameEN;
num? price;
int? rowID;
ServicesPriceListResponseModel({
this.createdBy,
this.createdOn,
this.editedBy,
this.editedOn,
this.id,
this.isEnabled,
this.nameAR,
this.nameEN,
this.price,
this.rowID,
});
ServicesPriceListResponseModel.fromJson(Map<String, dynamic> json) {
createdBy = json['CreatedBy'];
createdOn = json['CreatedOn'];
editedBy = json['EditedBy'];
editedOn = json['EditedOn'];
id = json['ID'];
isEnabled = json['IsEnabled'];
nameAR = json['NameAR'];
nameEN = json['NameEN'];
price = json['Price'];
rowID = json['RowID'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['CreatedBy'] = createdBy;
data['CreatedOn'] = createdOn;
data['EditedBy'] = editedBy;
data['EditedOn'] = editedOn;
data['ID'] = id;
data['IsEnabled'] = isEnabled;
data['NameAR'] = nameAR;
data['NameEN'] = nameEN;
data['Price'] = price;
data['RowID'] = rowID;
return data;
}
}

@ -0,0 +1,80 @@
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/services_price_list/models/resp_models/services_price_list_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class ServicesPriceListRepo {
Future<Either<Failure, GenericApiModel<List<ServicesPriceListResponseModel>>>> getServicesPriceList({
String searchKey = "",
int pageIndex = 0,
int pageSize = 0,
});
}
class ServicesPriceListRepoImp implements ServicesPriceListRepo {
final ApiClient apiClient;
final LoggerService loggerService;
ServicesPriceListRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<ServicesPriceListResponseModel>>>> getServicesPriceList({
String searchKey = "",
int pageIndex = 0,
int pageSize = 0,
}) async {
Map<String, dynamic> mapDevice = {
"ID": 1,
"SearchKey": searchKey,
"PageIndex": pageIndex,
"PageSize": pageSize,
"RowCount": 0,
"TokenID": "@dm!n"
};
try {
GenericApiModel<List<ServicesPriceListResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_SERVICES_PRICE_LIST,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['getServicesPriceList'];
if (list == null || list.isEmpty) {
throw Exception("Services price list is empty");
}
final servicesList = list
.map((item) => ServicesPriceListResponseModel.fromJson(item as Map<String, dynamic>))
.toList()
.cast<ServicesPriceListResponseModel>();
apiResponse = GenericApiModel<List<ServicesPriceListResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: servicesList,
);
} 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()));
}
}
}

@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/services_price_list/models/resp_models/services_price_list_response_model.dart';
import 'package:hmg_patient_app_new/features/services_price_list/services_price_list_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class ServicesPriceListViewModel extends ChangeNotifier {
bool isServicesPriceListLoading = false;
ServicesPriceListRepo servicesPriceListRepo;
ErrorHandlerService errorHandlerService;
List<ServicesPriceListResponseModel> servicesPriceList = [];
List<ServicesPriceListResponseModel> filteredServicesPriceList = [];
String searchKey = "";
ServicesPriceListViewModel({
required this.servicesPriceListRepo,
required this.errorHandlerService,
});
initServicesPriceListProvider() {
servicesPriceList.clear();
filteredServicesPriceList.clear();
isServicesPriceListLoading = true;
getServicesPriceList();
notifyListeners();
}
setIsServicesPriceListLoading(bool val) {
isServicesPriceListLoading = val;
notifyListeners();
}
setSearchKey(String val) {
searchKey = val;
filterServicesList(val);
}
void filterServicesList(String query) {
if (query.isEmpty) {
filteredServicesPriceList = List.from(servicesPriceList);
} else {
filteredServicesPriceList = servicesPriceList.where((service) {
final nameEN = service.nameEN?.toLowerCase() ?? '';
final nameAR = service.nameAR?.toLowerCase() ?? '';
final searchLower = query.toLowerCase();
return nameEN.contains(searchLower) || nameAR.contains(searchLower);
}).toList();
}
notifyListeners();
}
Future<void> getServicesPriceList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await servicesPriceListRepo.getServicesPriceList(
searchKey: searchKey,
);
result.fold(
(failure) async {
isServicesPriceListLoading = false;
notifyListeners();
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
isServicesPriceListLoading = false;
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? "Error loading services price list");
}
} else if (apiResponse.messageStatus == 1) {
servicesPriceList = apiResponse.data!;
servicesPriceList.removeWhere((element) => element.isEnabled == false);
filteredServicesPriceList = List.from(servicesPriceList);
isServicesPriceListLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -84,9 +84,9 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
String? _selectedGender; String? _selectedGender;
DateTime? _dateOfBirth; DateTime? _dateOfBirth;
int? _selectedAge; int? _selectedAge;
double _selectedHeight = 170; num _selectedHeight = 170;
bool _isHeightCm = true; bool _isHeightCm = true;
double _selectedWeight = 60; num _selectedWeight = 60;
bool _isWeightKg = true; bool _isWeightKg = true;
// Getters // Getters
@ -104,11 +104,25 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
int? get selectedAge => _selectedAge; int? get selectedAge => _selectedAge;
double? get selectedHeight => _selectedHeight; setSelectedAge(int age) {
_selectedAge = age;
}
setSelectedHeight(num height) {
_selectedHeight = height;
notifyListeners();
}
setSelectedWeight(num weight) {
_selectedWeight = weight;
notifyListeners();
}
num? get selectedHeight => _selectedHeight;
bool get isHeightCm => _isHeightCm; bool get isHeightCm => _isHeightCm;
double? get selectedWeight => _selectedWeight; num? get selectedWeight => _selectedWeight;
bool get isWeightKg => _isWeightKg; bool get isWeightKg => _isWeightKg;

@ -13,17 +13,20 @@ class TodoSectionViewModel extends ChangeNotifier {
String? notificationsCount = "0"; String? notificationsCount = "0";
initializeTodoSectionViewModel() async { initializeTodoSectionViewModel() async {
patientAncillaryOrdersList.clear(); if (isAncillaryOrdersNeedReloading) {
isAncillaryOrdersLoading = true; patientAncillaryOrdersList.clear();
isAncillaryDetailsProceduresLoading = true; isAncillaryOrdersLoading = true;
notificationsCount = "0"; isAncillaryDetailsProceduresLoading = true;
getPatientOnlineAncillaryOrderList(); notificationsCount = "0";
getPatientOnlineAncillaryOrderList();
}
getPatientDashboard(); getPatientDashboard();
} }
bool isAncillaryOrdersLoading = false; bool isAncillaryOrdersLoading = false;
bool isAncillaryDetailsProceduresLoading = false; bool isAncillaryDetailsProceduresLoading = false;
bool isProcessingPayment = false; bool isProcessingPayment = false;
bool isAncillaryOrdersNeedReloading = true;
List<AncillaryOrderItem> patientAncillaryOrdersList = []; List<AncillaryOrderItem> patientAncillaryOrdersList = [];
List<AncillaryOrderProcedureItem> patientAncillaryOrderProceduresList = []; List<AncillaryOrderProcedureItem> patientAncillaryOrderProceduresList = [];
@ -32,6 +35,11 @@ class TodoSectionViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void setIsAncillaryOrdersNeedReloading(bool value) {
isAncillaryOrdersNeedReloading = value;
notifyListeners();
}
Future<void> getPatientDashboard({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientDashboard({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await todoSectionRepo.getPatientDashboard(); final result = await todoSectionRepo.getPatientDashboard();
@ -55,6 +63,10 @@ class TodoSectionViewModel extends ChangeNotifier {
} }
Future<void> getPatientOnlineAncillaryOrderList({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientOnlineAncillaryOrderList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isAncillaryOrdersNeedReloading) {
return;
}
patientAncillaryOrdersList.clear(); patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true; isAncillaryOrdersLoading = true;
notifyListeners(); notifyListeners();
@ -71,6 +83,7 @@ class TodoSectionViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
patientAncillaryOrdersList = apiResponse.data!; patientAncillaryOrdersList = apiResponse.data!;
isAncillaryOrdersLoading = false; isAncillaryOrdersLoading = false;
isAncillaryOrdersNeedReloading = false;
notifyListeners(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);

@ -1553,5 +1553,14 @@ abstract class LocaleKeys {
static const contextDependent = 'contextDependent'; static const contextDependent = 'contextDependent';
static const calculatedValue = 'calculatedValue'; static const calculatedValue = 'calculatedValue';
static const calculationFormula = 'calculationFormula'; static const calculationFormula = 'calculationFormula';
static const confirmYourLocation = 'confirmYourLocation';
static const confirmYourLocationDesc = 'confirmYourLocationDesc';
static const insideHospital = 'insideHospital';
static const outsideHospital = 'outsideHospital';
static const servicePriceList = 'servicePriceList';
static const servicePriceListDesc = 'servicePriceListDesc';
static const servicePriceListRights = 'servicePriceListRights';
static const serviceName = 'serviceName';
static const noFreeSlot = 'noFreeSlot';
} }

@ -36,6 +36,7 @@ import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_mo
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart'; import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
import 'package:hmg_patient_app_new/features/qr_parking/qr_parking_view_model.dart'; import 'package:hmg_patient_app_new/features/qr_parking/qr_parking_view_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/features/services_price_list/services_price_list_view_model.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart'; import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart';
import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart'; import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart'; import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
@ -202,6 +203,9 @@ void main() async {
), ),
ChangeNotifierProvider<AskDoctorViewModel>( ChangeNotifierProvider<AskDoctorViewModel>(
create: (_) => getIt.get<AskDoctorViewModel>(), create: (_) => getIt.get<AskDoctorViewModel>(),
),
ChangeNotifierProvider<ServicesPriceListViewModel>(
create: (_) => getIt.get<ServicesPriceListViewModel>(),
) )
], child: MyApp()), ], child: MyApp()),
), // Wrap your app ), // Wrap your app
@ -224,7 +228,7 @@ class MyApp extends StatelessWidget {
return MaterialApp( return MaterialApp(
title: 'Dr. AlHabib', title: 'Dr. AlHabib',
builder: (context, mchild) { builder: (context, mchild) {
return MediaQuery(data: MediaQuery.of(context).copyWith(textScaler: TextScaler.linear(1.0)), child: mchild!); return MediaQuery(data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling, alwaysUse24HourFormat: true,), child: mchild!, );
}, },
showSemanticsDebugger: false, showSemanticsDebugger: false,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -8,6 +9,7 @@ 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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/calender_utils_new.dart'; import 'package:hmg_patient_app_new/core/utils/calender_utils_new.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/penguin_method_channel.dart';
import 'package:hmg_patient_app_new/core/utils/size_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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -15,6 +17,8 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart'; import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/feedback_type.dart';
import 'package:hmg_patient_app_new/features/hospital/AppPermission.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
@ -37,7 +41,8 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:maps_launcher/maps_launcher.dart'; import 'package:map_launcher/map_launcher.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../medical_file/widgets/medical_file_card.dart'; import '../medical_file/widgets/medical_file_card.dart';
@ -59,6 +64,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
late LabViewModel labViewModel; late LabViewModel labViewModel;
late RadiologyViewModel radiologyViewModel; late RadiologyViewModel radiologyViewModel;
String mapURL = "";
@override @override
void initState() { void initState() {
scheduleMicrotask(() async { scheduleMicrotask(() async {
@ -90,6 +97,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
title: LocaleKeys.appointmentDetails.tr(context: context), title: LocaleKeys.appointmentDetails.tr(context: context),
report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? () { ? () {
contactUsViewModel.setSelectedFeedbackType(FeedbackType(id: 1, nameEN: "Complaint for appointment", nameAR: 'شكوى على موعد'));
contactUsViewModel.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel); contactUsViewModel.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel);
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
@ -103,6 +111,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AppointmentDoctorCard( AppointmentDoctorCard(
renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ||
(widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) ||
!Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)),
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onAskDoctorTap: () async { onAskDoctorTap: () async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingDoctorAvailability.tr(context: context)); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingDoctorAvailability.tr(context: context));
@ -180,15 +191,57 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
LocaleKeys.appointmentStatus.tr(context: context).toText16(isBold: true), Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.appointmentStatus.tr(context: context).toText16(isBold: true),
SizedBox(height: 4.h),
(!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel)
? LocaleKeys.notConfirmed.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500)
: LocaleKeys.confirmed.tr(context: context).toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)),
SizedBox(height: 16.h),
],
),
((!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel) && widget.patientAppointmentHistoryResponseModel.nextAction != 10)
? CustomButton(
text: LocaleKeys.confirm.tr(),
onPressed: () async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingAppointmentPleaseWait.tr(context: context));
await myAppointmentsViewModel.confirmAppointment(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) {
LoaderBottomSheet.hideLoader();
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
showCommonBottomSheetWithoutHeight(
title: "",
context,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)),
callBackFunc: () {
Navigator.of(context).pop();
},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
backgroundColor: AppColors.successColor,
borderColor: AppColors.successColor,
textColor: AppColors.whiteColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
height: 40.h,
icon: AppAssets.confirm_appointment_icon,
iconColor: AppColors.whiteColor,
iconSize: 16.h,
)
: SizedBox.shrink())
], ],
), ),
SizedBox(height: 4.h),
(!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel)
? LocaleKeys.notConfirmed.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500)
: LocaleKeys.confirmed.tr(context: context).toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)),
SizedBox(height: 16.h),
//TODO Add countdown timer in case of LiveCare Appointment //TODO Add countdown timer in case of LiveCare Appointment
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false
? Row( ? Row(
@ -212,7 +265,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
borderRadius: BorderRadius.circular(24.r), borderRadius: BorderRadius.circular(24.r),
// Todo: what is this???? Api Key??? 😲 // Todo: what is this???? Api Key??? 😲
child: Image.network( child: Image.network(
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}", "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x${(MediaQuery.of(context).size.height * 0.35).toInt()}&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}",
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
), ),
@ -221,9 +274,24 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
child: SizedBox( child: SizedBox(
width: MediaQuery.of(context).size.width * 0.785, width: MediaQuery.of(context).size.width * 0.785,
child: CustomButton( child: CustomButton(
onPressed: () { onPressed: () async {
MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), if (widget.patientAppointmentHistoryResponseModel.projectID == 130 || widget.patientAppointmentHistoryResponseModel.projectID == 120) {
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName); showDirectionsBottomSheet();
} else {
await MapLauncher.showMarker(
mapType: MapType.google,
coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!),
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)),
title: widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital",
).catchError((err) {
MapLauncher.showMarker(
mapType: Platform.isIOS ? MapType.apple : MapType.google,
coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!),
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)),
title: widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital",
);
});
}
}, },
text: LocaleKeys.getDirections.tr(context: context), text: LocaleKeys.getDirections.tr(context: context),
backgroundColor: AppColors.textColor.withValues(alpha: 0.8), backgroundColor: AppColors.textColor.withValues(alpha: 0.8),
@ -751,11 +819,15 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
], ],
).paddingOnly(left: 16.h, top: 24.h, right: 16.h, bottom: 0.h), ).paddingOnly(left: 16.h, top: 24.h, right: 16.h, bottom: 0.h),
AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? CustomButton( ?
text: LocaleKeys.rebookAppointment.tr(context: context), (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ||
onPressed: () { widget.patientAppointmentHistoryResponseModel.isExecludeDoctor! ||
openDoctorScheduleCalendar(); Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID))
}, ? CustomButton(
text: LocaleKeys.rebookAppointment.tr(context: context),
onPressed: () {
openDoctorScheduleCalendar();
},
backgroundColor: AppColors.successColor, backgroundColor: AppColors.successColor,
borderColor: AppColors.successColor, borderColor: AppColors.successColor,
textColor: AppColors.whiteColor, textColor: AppColors.whiteColor,
@ -767,11 +839,11 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
icon: AppAssets.add_icon, icon: AppAssets.add_icon,
iconColor: AppColors.whiteColor, iconColor: AppColors.whiteColor,
iconSize: 18.h, iconSize: 18.h,
).paddingSymmetrical(16.h, 24.h) ).paddingSymmetrical(16.h, 24.h)
: SizedBox.shrink()
: CustomButton( : CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () { onPressed: () {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction); handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction);
}, },
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction), backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction),
@ -796,6 +868,89 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
); );
} }
void showDirectionsBottomSheet() {
showCommonBottomSheetWithoutHeight(
context,
title: "",
isDismissible: true,
isCloseButtonVisible: false,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.confirmYourLocation.tr(context: context).toText28(color: AppColors.textColor, isBold: true, height: 1.5),
SizedBox(height: 4.h),
LocaleKeys.confirmYourLocationDesc.tr(context: context).toText12(
color: AppColors.greyTextColor,
fontWeight: FontWeight.w500,
),
SizedBox(height: 24.h),
CustomButton(
text: LocaleKeys.insideHospital.tr(context: context),
onPressed: () {
Navigator.pop(context);
initPenguinSDK(widget.patientAppointmentHistoryResponseModel.projectID == 130 ? 1 : (widget.patientAppointmentHistoryResponseModel.projectID == 120 ? 3 : -1),
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID.toString());
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 56.h,
),
SizedBox(height: 8.h),
CustomButton(
text: LocaleKeys.outsideHospital.tr(context: context),
onPressed: () async {
Navigator.pop(context);
await MapLauncher.showMarker(
mapType: MapType.google,
coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)),
title: widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital",
).catchError((err) {
MapLauncher.showMarker(
mapType: Platform.isIOS ? MapType.apple : MapType.google,
coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)),
title: widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital",
);
});
},
backgroundColor: AppColors.chipSecondaryLightRedColor,
borderColor: AppColors.chipSecondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 56.h,
),
],
).paddingSymmetrical(24.w, 20.h),
callBackFunc: () {},
);
}
initPenguinSDK(int projectID, {String clinicID = ""}) async {
NavigationClinicDetails data = NavigationClinicDetails();
data.projectId = projectID.toString();
data.clinicId = clinicID;
// data.clinicId = "1";
final bool permited = await AppPermission.askPenguinPermissions();
if (!permited) {
Map<Permission, PermissionStatus> statuses = await [
Permission.location,
Permission.bluetooth,
Permission.bluetoothConnect,
Permission.bluetoothScan,
Permission.activityRecognition,
].request().whenComplete(() {
PenguinMethodChannel().launch("penguin", getIt.get<AppState>().isArabic() ? "ar" : "en", getIt.get<AppState>().getAuthenticatedUser()?.patientId?.toString() ?? "", true, details: data);
});
}
}
openDoctorScheduleCalendar() async { openDoctorScheduleCalendar() async {
DoctorsListResponseModel doctor = DoctorsListResponseModel( DoctorsListResponseModel doctor = DoctorsListResponseModel(
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
@ -849,6 +1004,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.getPatientAppointments(true, false); myAppointmentsViewModel.getPatientAppointments(true, false);
showCommonBottomSheet(context, child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), callBackFunc: (str) { showCommonBottomSheet(context, child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), callBackFunc: (str) {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
title: "", title: "",

@ -403,6 +403,9 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) { onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () { Future.delayed(Duration(milliseconds: 500), () {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,
@ -421,6 +424,9 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!, isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!,
onSuccess: (apiResponse) { onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () { Future.delayed(Duration(milliseconds: 500), () {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,
@ -459,7 +465,6 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
await payfortViewModel.checkPaymentStatus( await payfortViewModel.checkPaymentStatus(
transactionID: transID, transactionID: transID,
onSuccess: (apiResponse) async { onSuccess: (apiResponse) async {
print(apiResponse.data);
if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") { if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") {
await myAppointmentsViewModel.createAdvancePayment( await myAppointmentsViewModel.createAdvancePayment(
paymentMethodName: selectedPaymentMethod, paymentMethodName: selectedPaymentMethod,
@ -486,6 +491,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) { onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () { Future.delayed(Duration(milliseconds: 500), () {
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,
@ -504,6 +511,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!, isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!,
onSuccess: (apiResponse) { onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () { Future.delayed(Duration(milliseconds: 500), () {
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,
@ -517,6 +526,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
}); });
}); });
} else { } else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)), child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
@ -564,14 +574,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
} }
startApplePay() async { startApplePay() async {
showCommonBottomSheet(context, LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context));
child: Utils.getLoadingWidget(),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false);
transID = Utils.getAppointmentTransID( transID = Utils.getAppointmentTransID(
widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.projectID,
widget.patientAppointmentHistoryResponseModel.clinicID, widget.patientAppointmentHistoryResponseModel.clinicID,
@ -642,7 +645,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
onFailed: (failureResult) async { onFailed: (failureResult) async {
log("failureResult: ${failureResult.message.toString()}"); log("failureResult: ${failureResult.message.toString()}");
Navigator.of(context).pop(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: failureResult.message.toString()), child: Utils.getErrorWidget(loadingText: failureResult.message.toString()),
@ -652,6 +655,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
); );
}, },
onSucceeded: (successResult) async { onSucceeded: (successResult) async {
LoaderBottomSheet.hideLoader();
log("successResult: ${successResult.responseMessage.toString()}"); log("successResult: ${successResult.responseMessage.toString()}");
selectedPaymentMethod = successResult.paymentOption ?? "VISA"; selectedPaymentMethod = successResult.paymentOption ?? "VISA";
checkPaymentStatus(); checkPaymentStatus();

@ -26,6 +26,7 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'dart:ui' as ui;
class AppointmentCard extends StatelessWidget { class AppointmentCard extends StatelessWidget {
final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
@ -39,20 +40,20 @@ class AppointmentCard extends StatelessWidget {
final ContactUsViewModel? contactUsViewModel; final ContactUsViewModel? contactUsViewModel;
final BookAppointmentsViewModel bookAppointmentsViewModel; final BookAppointmentsViewModel bookAppointmentsViewModel;
final bool isForRate; final bool isForRate;
const AppointmentCard({
super.key, const AppointmentCard(
required this.patientAppointmentHistoryResponseModel, {super.key,
required this.myAppointmentsViewModel, required this.patientAppointmentHistoryResponseModel,
required this.bookAppointmentsViewModel, required this.myAppointmentsViewModel,
this.isLoading = false, required this.bookAppointmentsViewModel,
this.isFromHomePage = false, this.isLoading = false,
this.isFromMedicalReport = false, this.isFromHomePage = false,
this.isForEyeMeasurements = false, this.isFromMedicalReport = false,
this.isForFeedback = false, this.isForEyeMeasurements = false,
this.medicalFileViewModel, this.isForFeedback = false,
this.contactUsViewModel, this.medicalFileViewModel,
this.isForRate =false this.contactUsViewModel,
}); this.isForRate = false});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -64,11 +65,11 @@ class AppointmentCard extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
isForRate ? SizedBox(): _buildHeader(context, appState), isForRate ? SizedBox() : _buildHeader(context, appState),
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildDoctorRow(context), _buildDoctorRow(context),
SizedBox(height: 16.h), SizedBox(height: 16.h),
isForRate ? SizedBox(): _buildActionArea(context, appState), isForRate ? SizedBox() : _buildActionArea(context, appState),
], ],
), ),
), ),
@ -101,18 +102,13 @@ class AppointmentCard extends StatelessWidget {
textColor: isLoading ? AppColors.textColor : (isLiveCare ? AppColors.whiteColor : AppColors.textColor), textColor: isLoading ? AppColors.textColor : (isLiveCare ? AppColors.whiteColor : AppColors.textColor),
).toShimmer2(isShow: isLoading), ).toShimmer2(isShow: isLoading),
AppCustomChipWidget( AppCustomChipWidget(
labelText: isLoading labelText:
? 'OutPatient' isLoading ? 'OutPatient' : (appState.isArabic() ? patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! : patientAppointmentHistoryResponseModel.isInOutPatientDescription!),
: (appState.isArabic()
? patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN!
: patientAppointmentHistoryResponseModel.isInOutPatientDescription!),
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1), backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
textColor: AppColors.primaryRedColor, textColor: AppColors.primaryRedColor,
).toShimmer2(isShow: isLoading), ).toShimmer2(isShow: isLoading),
AppCustomChipWidget( AppCustomChipWidget(
labelText: isLoading labelText: isLoading ? 'Booked' : AppointmentType.getAppointmentStatusType(patientAppointmentHistoryResponseModel.patientStatusType!),
? 'Booked'
: AppointmentType.getAppointmentStatusType(patientAppointmentHistoryResponseModel.patientStatusType!),
backgroundColor: AppColors.successColor.withValues(alpha: 0.1), backgroundColor: AppColors.successColor.withValues(alpha: 0.1),
textColor: AppColors.successColor, textColor: AppColors.successColor,
).toShimmer2(isShow: isLoading), ).toShimmer2(isShow: isLoading),
@ -151,7 +147,9 @@ class AppointmentCard extends StatelessWidget {
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h), Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h),
SizedBox(height: 2.h), SizedBox(height: 2.h),
"${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor), (isFoldable || isTablet)
? "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor)
: "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(isBold: true, color: AppColors.textColor),
], ],
), ),
).circle(100).toShimmer2(isShow: isLoading), ).circle(100).toShimmer2(isShow: isLoading),
@ -182,17 +180,21 @@ class AppointmentCard extends StatelessWidget {
AppCustomChipWidget( AppCustomChipWidget(
labelText: isLoading labelText: isLoading
? 'Olaya' ? 'Olaya'
: patientAppointmentHistoryResponseModel.projectName!.length > 15 : (patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital").length > 15
? '${patientAppointmentHistoryResponseModel.projectName!.substring(0, 15)}...' ? '${(patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital").substring(0, 15)}...'
: patientAppointmentHistoryResponseModel.projectName!) : patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital")
.toShimmer2(isShow: isLoading), .toShimmer2(isShow: isLoading),
AppCustomChipWidget( Directionality(
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), textDirection: ui.TextDirection.ltr,
icon: AppAssets.appointment_calendar_icon, child: AppCustomChipWidget(
labelText: isLoading labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w),
? 'Cardiology' icon: AppAssets.appointment_calendar_icon,
: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}", richText: isLoading
).toShimmer2(isShow: isLoading), ? 'Cardiology'.toText10().toShimmer2(isShow: isLoading)
: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}"
.toText10(isEnglishOnly: true),
),
),
// AppCustomChipWidget( // AppCustomChipWidget(
// labelPadding: EdgeInsetsDirectional.only(start: -2.w, end: 6.w), // labelPadding: EdgeInsetsDirectional.only(start: -2.w, end: 6.w),
@ -224,83 +226,123 @@ class AppointmentCard extends StatelessWidget {
} }
Widget _buildActionArea(BuildContext context, AppState appState) { Widget _buildActionArea(BuildContext context, AppState appState) {
if (isFromMedicalReport) { if ((((patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ||
if (isForEyeMeasurements) { (patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) ||
return SizedBox.shrink(); !Utils.isClinicAllowedForRebook(patientAppointmentHistoryResponseModel.clinicID ?? 0))) &&
} else { AppointmentType.isArrived(patientAppointmentHistoryResponseModel)) {
return CustomButton( return CustomButton(
text: LocaleKeys.selectAppointment.tr(context: context), text: isFromMedicalReport ? LocaleKeys.selectAppointment.tr(context: context) : LocaleKeys.viewDetails.tr(context: context),
onPressed: () { onPressed: () {
if (isFromMedicalReport) {
if (isForFeedback) { if (isForFeedback) {
contactUsViewModel!.setPatientFeedbackSelectedAppointment(patientAppointmentHistoryResponseModel); contactUsViewModel!.setPatientFeedbackSelectedAppointment(patientAppointmentHistoryResponseModel);
} else { } else {
medicalFileViewModel!.setSelectedMedicalReportAppointment(patientAppointmentHistoryResponseModel); medicalFileViewModel!.setSelectedMedicalReportAppointment(patientAppointmentHistoryResponseModel);
} }
Navigator.pop(context, false); Navigator.pop(context, false);
}, } else {
backgroundColor: AppColors.secondaryLightRedColor, Navigator.of(context)
borderColor: AppColors.secondaryLightRedColor, .push(
textColor: AppColors.primaryRedColor, CustomPageRoute(
fontSize: 14.f, page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel),
fontWeight: FontWeight.w500, ),
borderRadius: 12.r, )
padding: EdgeInsets.symmetric(horizontal: 10.w), .then((_) {
// height: isTablet || isFoldable ? 46.h : 40.h, myAppointmentsViewModel.initAppointmentsViewModel();
height: 40.h, myAppointmentsViewModel.getPatientAppointments(true, false);
icon: AppAssets.checkmark_icon, });
iconColor: AppColors.primaryRedColor, }
iconSize: 16.h, },
); backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
// height: isTablet || isFoldable ? 46.h : 40.h,
height: 40.h,
icon: isFromMedicalReport ? AppAssets.checkmark_icon : null,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
);
} else {
if (isFromMedicalReport) {
if (isForEyeMeasurements) {
return SizedBox.shrink();
} else {
return CustomButton(
text: LocaleKeys.selectAppointment.tr(context: context),
onPressed: () {
if (isForFeedback) {
contactUsViewModel!.setPatientFeedbackSelectedAppointment(patientAppointmentHistoryResponseModel);
} else {
medicalFileViewModel!.setSelectedMedicalReportAppointment(patientAppointmentHistoryResponseModel);
}
Navigator.pop(context, false);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
// height: isTablet || isFoldable ? 46.h : 40.h,
height: 40.h,
icon: AppAssets.checkmark_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
);
}
} }
}
return Row( return Row(
children: [ children: [
Expanded( Expanded(
flex: 6, flex: 6,
child: (AppointmentType.isArrived(patientAppointmentHistoryResponseModel) child: (AppointmentType.isArrived(patientAppointmentHistoryResponseModel)
? _getArrivedButton(context) ? _getArrivedButton(context)
: CustomButton( : CustomButton(
text: AppointmentType.getNextActionText(patientAppointmentHistoryResponseModel.nextAction), text: AppointmentType.getNextActionText(patientAppointmentHistoryResponseModel.nextAction),
onPressed: () => _goToDetails(context), onPressed: () => _goToDetails(context),
backgroundColor: backgroundColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15),
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15), borderColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01),
borderColor: textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction),
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01), fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction), fontWeight: FontWeight.w500,
fontSize: 14.f, borderRadius: 12.r,
fontWeight: FontWeight.w500, padding: EdgeInsets.symmetric(horizontal: 10.w),
borderRadius: 12.r, height: 40.h,
padding: EdgeInsets.symmetric(horizontal: 10.w), icon: AppointmentType.getNextActionIcon(patientAppointmentHistoryResponseModel.nextAction),
height: 40.h, iconColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction),
icon: AppointmentType.getNextActionIcon(patientAppointmentHistoryResponseModel.nextAction), iconSize: 15.h,
iconColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction), ))
iconSize: 15.h, .toShimmer2(isShow: isLoading),
)) ),
.toShimmer2(isShow: isLoading), SizedBox(width: 8.h),
), Expanded(
SizedBox(width: 8.h), flex: 1,
Expanded( child: Container(
flex: 1, height: (isFoldable || isTablet) ? 50.h : 40.h,
child: Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.textColor, borderRadius: 10.h),
height: (isFoldable || isTablet) ? 50.h : 40.h, child: Transform.flip(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.textColor, borderRadius: 10.h), flipX: appState.isArabic(),
child: Transform.flip( child: Utils.buildSvgWithAssets(
flipX: appState.isArabic(), icon: AppAssets.forward_arrow_icon,
child: Utils.buildSvgWithAssets( iconColor: AppColors.whiteColor,
icon: AppAssets.forward_arrow_icon, width: 24.w,
iconColor: AppColors.whiteColor, height: 24.h,
width: 24.w, fit: BoxFit.contain,
height: 24.h, ),
fit: BoxFit.contain,
), ),
), ).toShimmer2(isShow: isLoading).onPress(() {
).toShimmer2(isShow: isLoading).onPress(() { _goToDetails(context);
_goToDetails(context); }),
}), ),
), ],
], );
); }
} }
Widget _getArrivedButton(BuildContext context) { Widget _getArrivedButton(BuildContext context) {
@ -340,7 +382,7 @@ class AppointmentCard extends StatelessWidget {
backgroundColor: AppColors.secondaryLightRedColor, backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor, textColor: AppColors.primaryRedColor,
fontSize: 14.f, fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.r, borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w), padding: EdgeInsets.symmetric(horizontal: 10.w),
@ -358,7 +400,7 @@ class AppointmentCard extends StatelessWidget {
backgroundColor: AppColors.greyColor, backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor, borderColor: AppColors.greyColor,
textColor: AppColors.blackColor, textColor: AppColors.blackColor,
fontSize: 14.f, fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.r, borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w), padding: EdgeInsets.symmetric(horizontal: 10.w),
@ -386,8 +428,8 @@ class AppointmentCard extends StatelessWidget {
), ),
) )
.then((_) { .then((_) {
myAppointmentsViewModel.initAppointmentsViewModel(); // myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false); // myAppointmentsViewModel.getPatientAppointments(true, false);
}); });
} }
} }

@ -36,6 +36,7 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
locationUtils = getIt.get<LocationUtils>();
appState = getIt.get<AppState>(); appState = getIt.get<AppState>();
FlutterNfcKit.nfcAvailability.then((value) { FlutterNfcKit.nfcAvailability.then((value) {
_supportsNFC = (value == NFCAvailability.available); _supportsNFC = (value == NFCAvailability.available);
@ -149,6 +150,9 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
scannedCode: scannedCode, scannedCode: scannedCode,
checkInType: checkInType, checkInType: checkInType,
onSuccess: (apiResponse) { onSuccess: (apiResponse) {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.success.tr(context: context), child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr()), callBackFunc: () async { showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.success.tr(context: context), child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr()), callBackFunc: () async {
await myAppointmentsViewModel.getPatientAppointmentQueueDetails(); await myAppointmentsViewModel.getPatientAppointmentQueueDetails();

@ -12,6 +12,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'dart:ui' as ui;
class AppointmentDoctorCard extends StatelessWidget { class AppointmentDoctorCard extends StatelessWidget {
const AppointmentDoctorCard( const AppointmentDoctorCard(
@ -89,25 +90,28 @@ class AppointmentDoctorCard extends StatelessWidget {
runSpacing: 6.h, runSpacing: 6.h,
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
labelText: (patientAppointmentHistoryResponseModel.clinicName!.length > 15 labelText: (patientAppointmentHistoryResponseModel.clinicName!.length > 20
? '${patientAppointmentHistoryResponseModel.clinicName!.substring(0, 12)}...' ? '${patientAppointmentHistoryResponseModel.clinicName!.substring(0, 20)}...'
: patientAppointmentHistoryResponseModel.clinicName!), : patientAppointmentHistoryResponseModel.clinicName!),
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w), labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
), ),
AppCustomChipWidget( AppCustomChipWidget(
labelText: patientAppointmentHistoryResponseModel.projectName!, labelText: patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital",
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
), ),
AppCustomChipWidget( Directionality(
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), textDirection: ui.TextDirection.ltr,
icon: AppAssets.doctor_calendar_icon, child: AppCustomChipWidget(
labelText: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang( labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w),
DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), icon: AppAssets.doctor_calendar_icon,
false, richText: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(
)}", DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate),
false,
)}".toText10(isEnglishOnly: true),
),
), ),
AppCustomChipWidget( AppCustomChipWidget(
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w),
icon: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! icon: !patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? AppAssets.walkin_appointment_icon ? AppAssets.walkin_appointment_icon
: AppAssets.small_livecare_icon, : AppAssets.small_livecare_icon,
@ -159,8 +163,9 @@ class AppointmentDoctorCard extends StatelessWidget {
icon: AppAssets.ask_doctor_icon, icon: AppAssets.ask_doctor_icon,
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
) )
: CustomButton( : !patientAppointmentHistoryResponseModel.isLiveCareAppointment!
text: LocaleKeys.rebookSameDoctor.tr(), ? CustomButton(
text: LocaleKeys.rebookSameDoctor.tr(),
onPressed: () { onPressed: () {
onRescheduleTap(); onRescheduleTap();
}, },
@ -175,7 +180,8 @@ class AppointmentDoctorCard extends StatelessWidget {
icon: AppAssets.rebook_appointment_icon, icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor, iconColor: AppColors.blackColor,
iconSize: 14.h, iconSize: 14.h,
); )
: SizedBox.shrink();
} else { } else {
return patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false return patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false
? CustomButton( ? CustomButton(

@ -73,6 +73,7 @@ class LoginScreenState extends State<LoginScreen> {
SizedBox(height: 130.h), // Adjusted to sizer unit SizedBox(height: 130.h), // Adjusted to sizer unit
LocaleKeys.welcomeToDrSulaiman.tr(context: context).toText32(isBold: true, color: AppColors.textColor), LocaleKeys.welcomeToDrSulaiman.tr(context: context).toText32(isBold: true, color: AppColors.textColor),
SizedBox(height: 32.h), SizedBox(height: 32.h),
Localizations.override(context: context, locale: Locale('en', 'US'), child: Container()), // Force English locale for this widget
TextInputWidget( TextInputWidget(
labelText: "${LocaleKeys.nationalId.tr(context: context)} / ${LocaleKeys.fileNo.tr(context: context)}", labelText: "${LocaleKeys.nationalId.tr(context: context)} / ${LocaleKeys.fileNo.tr(context: context)}",
hintText: "xxxxxxxxx", hintText: "xxxxxxxxx",
@ -89,6 +90,7 @@ class LoginScreenState extends State<LoginScreen> {
leadingIcon: AppAssets.student_card, leadingIcon: AppAssets.student_card,
errorMessage: LocaleKeys.enterValidIDorIqama.tr(context: context), errorMessage: LocaleKeys.enterValidIDorIqama.tr(context: context),
hasError: false, hasError: false,
fontFamily: "Poppins",
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
CustomButton( CustomButton(

@ -100,6 +100,7 @@ class _RegisterNew extends State<RegisterNew> {
hintText: "xxxxxxxxx", hintText: "xxxxxxxxx",
controller: authVm.nationalIdController, controller: authVm.nationalIdController,
focusNode: _nationalIdFocusNode, focusNode: _nationalIdFocusNode,
keyboardType: TextInputType.number,
isEnable: true, isEnable: true,
prefix: null, prefix: null,
isAllowRadius: true, isAllowRadius: true,
@ -108,6 +109,7 @@ class _RegisterNew extends State<RegisterNew> {
autoFocus: true, autoFocus: true,
padding: EdgeInsets.symmetric(vertical: 8.h), padding: EdgeInsets.symmetric(vertical: 8.h),
leadingIcon: AppAssets.student_card, leadingIcon: AppAssets.student_card,
fontFamily: "Poppins",
).withVerticalPadding(8), ).withVerticalPadding(8),
Divider(height: 1), Divider(height: 1),
TextInputWidget( TextInputWidget(
@ -125,6 +127,7 @@ class _RegisterNew extends State<RegisterNew> {
selectionType: SelectionTypeEnum.calendar, selectionType: SelectionTypeEnum.calendar,
onCalendarTypeChanged: authVm.onCalenderTypeChange, onCalendarTypeChanged: authVm.onCalenderTypeChange,
onChange: authVm.onDobChange, onChange: authVm.onDobChange,
fontFamily: "Poppins",
).withVerticalPadding(8), ).withVerticalPadding(8),
], ],
), ),

@ -18,6 +18,7 @@ import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dar
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'dart:ui' as ui;
class SavedLogin extends StatefulWidget { class SavedLogin extends StatefulWidget {
const SavedLogin({super.key}); const SavedLogin({super.key});
@ -93,14 +94,15 @@ class _SavedLogin extends State<SavedLogin> {
("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}") ("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}")
.toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1), .toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1),
appState.getSelectDeviceByImeiRespModelElement != null Directionality(
? (appState.getSelectDeviceByImeiRespModelElement!.createdOn != null textDirection: ui.TextDirection.ltr,
? DateUtil.getFormattedDate( child: appState.getSelectDeviceByImeiRespModelElement != null
DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), ? (appState.getSelectDeviceByImeiRespModelElement!.createdOn != null
"d MMMM, y 'at' HH:mm") ? DateUtil.getFormattedDate(DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), "d MMMM, y 'at' HH:mm")
: '--') : '--')
.toText16(isBold: true, color: AppColors.textColor) .toText16(isBold: true, color: AppColors.textColor, isEnglishOnly: true)
: SizedBox(), : SizedBox(),
),
appState.getSelectDeviceByImeiRespModelElement != null appState.getSelectDeviceByImeiRespModelElement != null
? Container( ? Container(

@ -263,13 +263,20 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
Row( Row(
children: [ children: [
Text( Text(
LocaleKeys.iAcceptThe.tr(), "${LocaleKeys.iAcceptThe.tr()} ",
style: context.dynamicTextStyle(fontSize: 14.f, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), style: context.dynamicTextStyle(fontSize: 14.f, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)),
), ),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
// Navigate to terms and conditions page // Navigate to terms and conditions page
Navigator.of(context).pushNamed('/terms'); // Navigator.of(context).pushNamed('/terms');
appState.isArabic()
? Utils.openWebView(
url: 'https://hmg.com/ar/Pages/Terms.aspx',
)
: Utils.openWebView(
url: 'https://hmg.com/en/Pages/Terms.aspx',
);
}, },
child: Text( child: Text(
LocaleKeys.termsConditoins.tr(), LocaleKeys.termsConditoins.tr(),

@ -458,8 +458,8 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
), ),
SizedBox(width: 16.w), SizedBox(width: 16.w),
CustomButton( CustomButton(
height: 40.h, height: 42.h,
width: 40.w, width: 42.w,
text: "", text: "",
onPressed: () => context.navigateWithName(AppRoutes.userInfoSelection), onPressed: () => context.navigateWithName(AppRoutes.userInfoSelection),
icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward, icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward,

@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/doctor_rating_details.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -108,9 +109,21 @@ class DoctorProfilePage extends StatelessWidget {
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
"Ratings".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), "Ratings".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate.toString().toText16(isBold: true, color: AppColors.textColor), bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate
.toString()
.toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor),
], ],
), ).onPress(() {
bookAppointmentsViewModel.getDoctorRatingDetails();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.doctorRating.tr(context: context),
context,
child: DoctorRatingDetails(),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}),
SizedBox(width: 36.w), SizedBox(width: 36.w),
Column( Column(
children: [ children: [
@ -122,15 +135,28 @@ class DoctorProfilePage extends StatelessWidget {
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
"Reviews".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), "Reviews".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate.toString().toText16(isBold: true, color: AppColors.textColor), bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate
.toString()
.toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor),
], ],
), ).onPress(() {
bookAppointmentsViewModel.getDoctorRatingDetails();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.doctorRating.tr(context: context),
context,
child: DoctorRatingDetails(),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}),
], ],
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h), SizedBox(height: 16.h),
"Biography".toText14(weight: FontWeight.w600, color: AppColors.textColor), LocaleKeys.docInfo.tr(context: context).toText14(weight: FontWeight.w600, color: AppColors.textColor),
SizedBox(height: 6.h),
bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo!.toText12(fontWeight: FontWeight.w600, color: AppColors.greyTextColor), bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo!.toText12(fontWeight: FontWeight.w600, color: AppColors.greyTextColor),
], ],
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),

@ -1,4 +1,3 @@
import 'dart:io'; import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@ -18,6 +17,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immed
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/services/livecare_permission_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -32,71 +32,30 @@ import 'package:smooth_corner/smooth_corner.dart';
class ImmediateLiveCarePaymentDetails extends StatelessWidget { class ImmediateLiveCarePaymentDetails extends StatelessWidget {
ImmediateLiveCarePaymentDetails({super.key}); ImmediateLiveCarePaymentDetails({super.key});
late ImmediateLiveCareViewModel immediateLiveCareViewModel; // late ImmediateLiveCareViewModel immediateLiveCareViewModel;
late AppState appState; late AppState appState;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); // immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false);
appState = getIt.get<AppState>(); appState = getIt.get<AppState>();
return Scaffold( return Scaffold(
backgroundColor: AppColors.scaffoldBgColor, backgroundColor: AppColors.scaffoldBgColor,
body: Column( body: Consumer<ImmediateLiveCareViewModel>(builder: (context, immediateLiveCareVM, child) {
children: [ return Column(
Expanded( children: [
child: CollapsingListView( Expanded(
title: LocaleKeys.reviewLiveCareRequest.tr(context: context), child: CollapsingListView(
child: SingleChildScrollView( title: LocaleKeys.reviewLiveCareRequest.tr(context: context),
padding: EdgeInsets.symmetric(horizontal: 24.h), child: SingleChildScrollView(
child: Column( padding: EdgeInsets.symmetric(horizontal: 24.h),
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
SizedBox(height: 24.h), children: [
LocaleKeys.patientInfo.tr(context: context).toText16(isBold: true), SizedBox(height: 24.h),
SizedBox(height: 16.h), LocaleKeys.patientInfo.tr(context: context).toText16(isBold: true),
Container( SizedBox(height: 16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( Container(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
children: [
Image.asset(
appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg,
width: 52.h,
height: 52.h,
),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} ${LocaleKeys.yearsOld.tr(context: context)}"),
AppCustomChipWidget(
labelText:
"${LocaleKeys.clinic.tr()}: ${(appState.isArabic() ? immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceNameN : immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceName)!}"),
],
),
],
),
],
),
),
),
SizedBox(height: 24.h),
LocaleKeys.selectedLiveCareType.tr(context: context).toText16(isBold: true),
SizedBox(height: 16.h),
Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: 24.h, borderRadius: 24.h,
@ -105,248 +64,371 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
child: Padding( child: Padding(
padding: EdgeInsets.all(16.h), padding: EdgeInsets.all(16.h),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Row( Image.asset(
appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg,
width: 52.h,
height: 52.h,
),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain), "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true),
SizedBox(width: 8.h), SizedBox(height: 8.h),
getLiveCareType(context, immediateLiveCareViewModel.liveCareSelectedCallType).toText16(isBold: true), Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} ${LocaleKeys.yearsOld.tr(context: context)}"),
AppCustomChipWidget(
labelText:
"${LocaleKeys.clinic.tr()}: ${(appState.isArabic() ? immediateLiveCareVM.immediateLiveCareSelectedClinic.serviceNameN : immediateLiveCareVM.immediateLiveCareSelectedClinic.serviceName)!}"),
],
),
], ],
), ),
Utils.buildSvgWithAssets(icon: AppAssets.edit_icon, width: 24.h, height: 24.h, fit: BoxFit.contain),
], ],
), ),
), ),
).onPress(() { ),
showCommonBottomSheetWithoutHeight(context, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareViewModel), callBackFunc: () async { SizedBox(height: 24.h),
debugPrint("Selected Call Type: ${immediateLiveCareViewModel.liveCareSelectedCallType}"); LocaleKeys.selectedLiveCareType.tr(context: context).toText16(isBold: true),
}, title: LocaleKeys.selectLiveCareCallType.tr(context: context), isCloseButtonVisible: true, isFullScreen: false); SizedBox(height: 16.h),
}); Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
}), return Container(
SizedBox(height: 24.h) decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
], color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain),
SizedBox(width: 8.h),
getLiveCareType(context, immediateLiveCareVM.liveCareSelectedCallType).toText16(isBold: true),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.edit_icon, width: 24.h, height: 24.h, fit: BoxFit.contain),
],
),
),
).onPress(() {
showCommonBottomSheetWithoutHeight(context, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareVM), callBackFunc: () async {
debugPrint("Selected Call Type: ${immediateLiveCareVM.liveCareSelectedCallType}");
}, title: LocaleKeys.selectLiveCareCallType.tr(context: context), isCloseButtonVisible: true, isFullScreen: false);
});
}),
SizedBox(height: 24.h)
],
),
), ),
), ),
), ),
), Container(
Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor,
color: AppColors.whiteColor, borderRadius: 24.h,
borderRadius: 24.h, hasShadow: false,
hasShadow: false, ),
), child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.isCash ?? true)
(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.isCash ?? true) ? Container(
? Container( height: 50.h,
height: 50.h, decoration: ShapeDecoration(
decoration: ShapeDecoration( color: AppColors.secondaryLightRedBorderColor,
color: AppColors.secondaryLightRedBorderColor, shape: SmoothRectangleBorder(
shape: SmoothRectangleBorder( borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)),
borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), smoothness: 1,
smoothness: 1, ),
), ),
), child: Row(
child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
children: [ LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h),
LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), CustomButton(
CustomButton( text: LocaleKeys.updateInsurance.tr(context: context),
text: LocaleKeys.updateInsurance.tr(context: context), onPressed: () {
onPressed: () { Navigator.of(context).push(
Navigator.of(context).push( CustomPageRoute(
CustomPageRoute( page: InsuranceHomePage(),
page: InsuranceHomePage(), ),
), );
); },
}, backgroundColor: AppColors.primaryRedColor,
backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.secondaryLightRedBorderColor,
borderColor: AppColors.secondaryLightRedBorderColor, textColor: AppColors.whiteColor,
textColor: AppColors.whiteColor, fontSize: 10,
fontSize: 10, fontWeight: FontWeight.w500,
fontWeight: FontWeight.w500, borderRadius: 8,
borderRadius: 8, padding: EdgeInsets.fromLTRB(15, 0, 15, 0),
padding: EdgeInsets.fromLTRB(15, 0, 15, 0), height: 30.h,
height: 30.h, ).paddingSymmetrical(24.h, 0.h),
).paddingSymmetrical(24.h, 0.h), ],
], ),
), )
) : const SizedBox(),
: const SizedBox(), SizedBox(height: 24.h),
SizedBox(height: 24.h), LocaleKeys.totalAmountToPay.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
LocaleKeys.totalAmountToPay.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.h, 0.h), SizedBox(height: 17.h),
SizedBox(height: 17.h), Row(
Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
children: [ LocaleKeys.amountBeforeTax.tr(context: context).toText14(isBold: true),
LocaleKeys.amountBeforeTax.tr(context: context).toText14(isBold: true), Utils.getPaymentAmountWithSymbol(immediateLiveCareVM.liveCareImmediateAppointmentFeesList.amount!.toText16(isBold: true), AppColors.blackColor, 13,
Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.amount!.toText16(isBold: true), AppColors.blackColor, 13, isSaudiCurrency: immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar" ||
isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "ريال"),
], ],
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
LocaleKeys.vat15.tr(context: context).toText14(isBold: true, color: AppColors.greyTextColor), LocaleKeys.vat15.tr(context: context).toText14(isBold: true, color: AppColors.greyTextColor),
Utils.getPaymentAmountWithSymbol( Utils.getPaymentAmountWithSymbol(
immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.tax!.toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13, immediateLiveCareVM.liveCareImmediateAppointmentFeesList.tax!.toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13,
isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), isSaudiCurrency: (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar" ||
], immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "ريال")),
).paddingSymmetrical(24.h, 0.h), ],
SizedBox(height: 17.h), ).paddingSymmetrical(24.h, 0.h),
Row( SizedBox(height: 17.h),
mainAxisAlignment: MainAxisAlignment.spaceBetween, Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
SizedBox(width: 150.h, child: Utils.getPaymentMethods()), children: [
Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!.toText24(isBold: true), AppColors.blackColor, 17, SizedBox(width: 150.h, child: Utils.getPaymentMethods()),
isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), Utils.getPaymentAmountWithSymbol(immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total!.toText24(isBold: true), AppColors.blackColor, 17,
], isSaudiCurrency: (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar" ||
).paddingSymmetrical(24.h, 0.h), immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "ريال")),
(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total == "0" || immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total == "0.0") ],
? CustomButton( ).paddingSymmetrical(24.h, 0.h),
text: LocaleKeys.confirmLiveCare.tr(context: context), (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0" || immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0.0")
onPressed: () async { ? CustomButton(
await askVideoCallPermission().then((val) async { text: LocaleKeys.confirmLiveCare.tr(context: context),
if (val) { onPressed: () async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingLiveCareRequest.tr(context: context)); await askVideoCallPermission(context).then((val) async {
if (val) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingLiveCareRequest.tr(context: context));
await immediateLiveCareViewModel.addNewCallRequestForImmediateLiveCare("${appState.getAuthenticatedUser()!.patientId}${DateTime.now().millisecondsSinceEpoch}"); await immediateLiveCareVM.addNewCallRequestForImmediateLiveCare("${appState.getAuthenticatedUser()!.patientId}${DateTime.now().millisecondsSinceEpoch}");
await immediateLiveCareViewModel.getPatientLiveCareHistory(); await immediateLiveCareVM.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) { if (immediateLiveCareVM.patientHasPendingLiveCareRequest) {
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: LandingNavigation(), page: ImmediateLiveCarePendingRequestPage(),
), ),
(r) => false); );
Navigator.of(context).push( } else {
CustomPageRoute( showCommonBottomSheetWithoutHeight(
page: ImmediateLiveCarePendingRequestPage(), context,
), child: Utils.getErrorWidget(loadingText: LocaleKeys.unknownErrorOccurred.tr(context: context)),
); callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
} else { } else {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context, context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.unknownErrorOccurred.tr(context: context)), child: Utils.getWarningWidget(
loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
isShowActionButtons: true,
onCancelTap: () {
Navigator.pop(context);
},
onConfirmTap: () async {
openAppSettings();
}),
callBackFunc: () {}, callBackFunc: () {},
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
); );
} }
} else { });
showCommonBottomSheetWithoutHeight( },
title: LocaleKeys.notice.tr(context: context), backgroundColor: AppColors.successColor,
context, borderColor: AppColors.successColor,
child: Utils.getWarningWidget( textColor: AppColors.whiteColor,
loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context), fontSize: 16,
isShowActionButtons: true, fontWeight: FontWeight.w500,
onCancelTap: () { borderRadius: 12,
Navigator.pop(context); padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
}, height: 50.h,
onConfirmTap: () async { icon: AppAssets.livecare_book_icon,
openAppSettings(); iconColor: AppColors.whiteColor,
}), iconSize: 18.h,
callBackFunc: () {}, ).paddingSymmetrical(24.h, 24.h)
isFullScreen: false, : CustomButton(
isCloseButtonVisible: true, text: LocaleKeys.payNow.tr(context: context),
); onPressed: () async {
} await askVideoCallPermission(context).then((val) {
}); if (val) {
}, Navigator.of(context).push(
backgroundColor: AppColors.successColor, CustomPageRoute(
borderColor: AppColors.successColor, page: ImmediateLiveCarePaymentPage(),
textColor: AppColors.whiteColor, ),
fontSize: 16, );
fontWeight: FontWeight.w500, }
borderRadius: 12, // else {
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // showCommonBottomSheetWithoutHeight(
height: 50.h, // title: LocaleKeys.notice.tr(context: context),
icon: AppAssets.livecare_book_icon, // context,
iconColor: AppColors.whiteColor, // child: Utils.getWarningWidget(
iconSize: 18.h, // loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
).paddingSymmetrical(24.h, 24.h) // isShowActionButtons: true,
: CustomButton( // onCancelTap: () {
text: LocaleKeys.payNow.tr(context: context), // Navigator.pop(context);
onPressed: () async { // },
await askVideoCallPermission().then((val) { // onConfirmTap: () async {
if (val) { // openAppSettings();
Navigator.of(context).push( // }),
CustomPageRoute( // callBackFunc: () {},
page: ImmediateLiveCarePaymentPage(), // isFullScreen: false,
), // isCloseButtonVisible: true,
); // );
} else { // }
showCommonBottomSheetWithoutHeight( });
title: LocaleKeys.notice.tr(context: context), },
context, backgroundColor: AppColors.infoColor,
child: Utils.getWarningWidget( borderColor: AppColors.infoColor,
loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context), textColor: AppColors.whiteColor,
isShowActionButtons: true, fontSize: 16,
onCancelTap: () { fontWeight: FontWeight.w500,
Navigator.pop(context); borderRadius: 12,
}, padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
onConfirmTap: () async { height: 50.h,
openAppSettings(); icon: AppAssets.appointment_pay_icon,
}), iconColor: AppColors.whiteColor,
callBackFunc: () {}, iconSize: 18.h,
isFullScreen: false, ).paddingSymmetrical(24.h, 24.h),
isCloseButtonVisible: true, ],
); ),
}
});
},
backgroundColor: AppColors.infoColor,
borderColor: AppColors.infoColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.appointment_pay_icon,
iconColor: AppColors.whiteColor,
iconSize: 18.h,
).paddingSymmetrical(24.h, 24.h),
],
), ),
), ],
], );
), }),
); );
} }
Future<bool> askVideoCallPermission() async { Future<bool> askVideoCallPermission(BuildContext context) async {
bool hasPermission = false; final statuses = await LiveCarePermissionService.instance.requestCameraMicAndNotification(context);
if (Platform.isIOS) {
Map<Permission, PermissionStatus> statuses = await [Permission.camera, Permission.microphone].request(); // If service returned nothing (error), treat as not granted
if (statuses.isEmpty) return false;
bool cameraGranted = statuses[Permission.camera]?.isGranted ?? false;
bool micGranted = statuses[Permission.microphone]?.isGranted ?? false;
bool notifGranted = statuses[Permission.notification]?.isGranted ?? false;
// If all required permissions are already granted
if (cameraGranted && micGranted && notifGranted) return true;
// Collect only the missing permissions
final missing = <Permission>[];
if (!cameraGranted) missing.add(Permission.camera);
if (!micGranted) missing.add(Permission.microphone);
if (!notifGranted) missing.add(Permission.notification);
// If any of the missing permissions are permanently denied/restricted -> open settings (single dialog)
final permanent = missing.where((p) => (statuses[p]?.isPermanentlyDenied ?? false) || (statuses[p]?.isRestricted ?? false)).toList();
if (permanent.isNotEmpty) {
final names = permanent.map((p) => LiveCarePermissionService.instance.friendlyName(p)).join(' and ');
final message = permanent.length == 1 ? '$names permission is permanently denied. Open app settings to allow it.' : '$names permissions are permanently denied. Open app settings to allow them.';
await LiveCarePermissionService.instance.showOpenSettingsDialog(
context,
title: "Permissions Required",
message: message,
);
return false;
}
// Show one combined retry dialog for all missing permissions
final missingNames = missing.map((p) => LiveCarePermissionService.instance.friendlyName(p)).join(' and ');
final retry = await showDialog<bool>(
context: context,
builder: (c) => AlertDialog(
title: const Text('Permissions required'),
content: Text('$missingNames permission${missing.length > 1 ? 's are' : ' is'} required to start the video call. Retry now?'),
actions: [
TextButton(onPressed: () => Navigator.of(c).pop(false), child: const Text('Cancel')),
TextButton(onPressed: () => Navigator.of(c).pop(true), child: const Text('Retry')),
],
),
);
if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted) { if (retry != true) return false;
// Camera permission granted
hasPermission = true;
} else {
hasPermission = false;
}
} else {
Map<Permission, PermissionStatus> statuses = await [Permission.camera, Permission.microphone, Permission.notification].request();
if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted && statuses[Permission.notification] == PermissionStatus.granted) { // Request only the missing permissions
// Camera permission granted final newStatuses = <Permission, PermissionStatus>{};
hasPermission = true; for (final p in missing) {
} else { newStatuses[p] = await p.request();
hasPermission = false;
}
} }
return hasPermission; // Update grant state (preserve previously granted ones)
cameraGranted = newStatuses[Permission.camera]?.isGranted ?? cameraGranted;
micGranted = newStatuses[Permission.microphone]?.isGranted ?? micGranted;
notifGranted = newStatuses[Permission.notification]?.isGranted ?? notifGranted;
// If any requested permission is now permanently denied -> open settings
final newlyPermanent = missing.where((p) => (newStatuses[p]?.isPermanentlyDenied ?? false) || (newStatuses[p]?.isRestricted ?? false)).toList();
if (newlyPermanent.isNotEmpty) {
final names = newlyPermanent.map((p) => LiveCarePermissionService.instance.friendlyName(p)).join(' and ');
final message =
newlyPermanent.length == 1 ? '$names permission is permanently denied. Open app settings to allow it.' : '$names permissions are permanently denied. Open app settings to allow them.';
await LiveCarePermissionService.instance.showOpenSettingsDialog(
context,
title: "Permissions Required",
message: message,
);
return false;
}
// if (!(await Permission.camera.request().isGranted) || !(await Permission.microphone.request().isGranted)) { return cameraGranted && micGranted && notifGranted;
// return false;
// }
} }
// Future<bool> askVideoCallPermission() async {
// bool hasPermission = false;
// if (Platform.isIOS) {
// Map<Permission, PermissionStatus> statuses = await [Permission.camera, Permission.microphone].request();
//
// if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted) {
// // Camera permission granted
// hasPermission = true;
// } else {
// hasPermission = false;
// }
// } else {
// Map<Permission, PermissionStatus> statuses = await [Permission.camera, Permission.microphone, Permission.notification].request();
//
// if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted && statuses[Permission.notification] == PermissionStatus.granted) {
// // Camera permission granted
// hasPermission = true;
// } else {
// hasPermission = false;
// }
// }
//
// return hasPermission;
//
// // if (!(await Permission.camera.request().isGranted) || !(await Permission.microphone.request().isGranted)) {
// // return false;
// // }
// }
String getLiveCareType(BuildContext context, int callType) { String getLiveCareType(BuildContext context, int callType) {
switch (callType) { switch (callType) {
case 1: case 1:

@ -432,6 +432,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
), ),
); );
} else { } else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: "Unknown error occurred..."), child: Utils.getErrorWidget(loadingText: "Unknown error occurred..."),
@ -441,6 +442,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
); );
} }
} else { } else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr()), child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr()),

@ -120,7 +120,7 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
), ),
AppCustomChipWidget( AppCustomChipWidget(
labelText: labelText:
"${LocaleKeys.date.tr(context: context)}: ${bookAppointmentsViewModel.isWaitingAppointmentSelected ? DateUtil.formatDateToDate(DateTime.now(), false) : bookAppointmentsViewModel.selectedAppointmentDate}", "${LocaleKeys.date.tr(context: context)}: ${bookAppointmentsViewModel.isWaitingAppointmentSelected ? DateUtil.formatDateToDate(DateTime.now(), false) : bookAppointmentsViewModel.selectedAppointmentDisplayDate}",
), ),
AppCustomChipWidget( AppCustomChipWidget(
labelText: labelText:
@ -178,7 +178,7 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
bookAppointmentsViewModel.selectedDoctor.projectName!.toText16(isBold: true), (bookAppointmentsViewModel.selectedDoctor.projectName ?? "Habib Hospital").toText16(isBold: true),
SizedBox(height: 8.h), SizedBox(height: 8.h),
bookAppointmentsViewModel.appointmentNearestGateResponseModel != null bookAppointmentsViewModel.appointmentNearestGateResponseModel != null
? Wrap( ? Wrap(
@ -286,8 +286,20 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
} else { } else {
//TODO: Add patient Derma package check API Here //TODO: Add patient Derma package check API Here
await bookAppointmentsViewModel.insertSpecificAppointment(onError: (err) { await bookAppointmentsViewModel.insertSpecificAppointment(onError: (err) {
print(err.data["ErrorEndUserMessage"]); // print(err.data["ErrorEndUserMessage"]);
LoadingUtils.hideFullScreenLoader(); LoadingUtils.hideFullScreenLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getErrorWidget(loadingText: err.toString()),
callBackFunc: () {
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
// Navigator.pop(context);
},
isFullScreen: false,
isCloseButtonVisible: true,
);
}, onSuccess: (apiResp) async { }, onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader(); LoadingUtils.hideFullScreenLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async { await Future.delayed(Duration(milliseconds: 50)).then((value) async {

@ -988,7 +988,7 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
void onLiveCareClinicSelected(GetLiveCareClinicsResponseModel clinic) { void onLiveCareClinicSelected(GetLiveCareClinicsResponseModel clinic) {
bookAppointmentsViewModel.setLiveCareSelectedClinic(clinic); bookAppointmentsViewModel.setLiveCareSelectedClinic(clinic);
bookAppointmentsViewModel.setIsDoctorsListLoading(true); bookAppointmentsViewModel.setIsLiveCareDoctorsListLoading(true);
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: SelectDoctorPage(), page: SelectDoctorPage(),

@ -75,7 +75,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView( body: CollapsingListView(
title: LocaleKeys.chooseDoctor.tr(), title: LocaleKeys.chooseDoctor.tr(context: context),
// bottomChild: Container( // bottomChild: Container(
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.only(topLeft: Radius.circular(24.r), topRight: Radius.circular(24.r))), // decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.only(topLeft: Radius.circular(24.r), topRight: Radius.circular(24.r))),
// padding: EdgeInsets.symmetric(vertical: 20.h, horizontal: 20.h), // padding: EdgeInsets.symmetric(vertical: 20.h, horizontal: 20.h),
@ -95,9 +95,59 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h), padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) { child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return Column( return bookAppointmentsViewModel.isLiveCareSchedule
crossAxisAlignment: CrossAxisAlignment.start, ? Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.separated(
padding: EdgeInsets.only(top: 16.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : (bookAppointmentsVM.liveCareDoctorsList.isNotEmpty ? bookAppointmentsVM.liveCareDoctorsList.length : 1),
itemBuilder: (context, index) {
// final isExpanded = bookAppointmentsVM.expandedGroupIndex == index;
final isExpanded = true;
return bookAppointmentsVM.isDoctorsListLoading
? DoctorCard(
doctorsListResponseModel: DoctorsListResponseModel(),
isLoading: true,
bookAppointmentsViewModel: bookAppointmentsViewModel,
)
: bookAppointmentsVM.liveCareDoctorsList.isEmpty
? Utils.getNoDataWidget(context, noDataText: LocaleKeys.noDoctorFound.tr())
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: Padding(
padding: EdgeInsets.all(16.h),
child: DoctorCard(
isLoading: bookAppointmentsVM.isClinicsListLoading,
doctorsListResponseModel: bookAppointmentsVM.liveCareDoctorsList[index],
bookAppointmentsViewModel: bookAppointmentsViewModel,
).onPress(() {
// onLiveCareClinicSelected(bookAppointmentsVM.liveCareClinicsList[index]);
}),
),
),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
SizedBox(height: 24.h),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h), SizedBox(height: 16.h),
Row( Row(
spacing: 8.h, spacing: 8.h,

@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.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/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
@ -144,13 +145,14 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
child: selectedDateDisplay.toText16(weight: FontWeight.w500), child: selectedDateDisplay.toText16(weight: FontWeight.w500),
), ),
//TODO: Add Next Day Span here //TODO: Add Next Day Span here
SizedBox( dayEvents.isNotEmpty
height: 100.h, ? SizedBox(
child: SingleChildScrollView( height: 100.h,
scrollDirection: Axis.vertical, child: SingleChildScrollView(
child: Wrap( scrollDirection: Axis.vertical,
direction: Axis.horizontal, child: Wrap(
alignment: WrapAlignment.start, direction: Axis.horizontal,
alignment: WrapAlignment.start,
spacing: 6.h, spacing: 6.h,
runSpacing: 6.h, runSpacing: 6.h,
children: List.generate( children: List.generate(
@ -168,10 +170,13 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
), ),
), ),
), ),
), )
: Utils.getNoDataWidget(context, noDataText: LocaleKeys.noFreeSlot.tr(context: context)),
SizedBox(height: 16.h), SizedBox(height: 16.h),
CustomButton( CustomButton(
text: LocaleKeys.select.tr(context: context), text: LocaleKeys.select.tr(context: context),
isDisabled: dayEvents.isEmpty,
onPressed: () async { onPressed: () async {
if (appState.isAuthenticated) { if (appState.isAuthenticated) {
if(selectedTime == LocaleKeys.waitingAppointment.tr(context: context)){ if(selectedTime == LocaleKeys.waitingAppointment.tr(context: context)){
@ -186,7 +191,7 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
); );
} else { } else {
bookAppointmentsViewModel.getAppointmentNearestGate(projectID: bookAppointmentsViewModel.selectedDoctor.projectID!, clinicID: bookAppointmentsViewModel.selectedDoctor.clinicID!); bookAppointmentsViewModel.getAppointmentNearestGate(projectID: bookAppointmentsViewModel.selectedDoctor.projectID!, clinicID: bookAppointmentsViewModel.selectedDoctor.clinicID!);
bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime); bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime, selectedDateDisplay);
Navigator.of(context).pop(); Navigator.of(context).pop();
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
@ -304,11 +309,14 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
Map<String, dynamic> timeSlot = {"isoTime": dayEvents[i].isoTime, "start": dayEvents[i].start.toString(), "end": dayEvents[i].end.toString(), "vidaDate": dayEvents[i].vidaDate}; Map<String, dynamic> timeSlot = {"isoTime": dayEvents[i].isoTime, "start": dayEvents[i].start.toString(), "end": dayEvents[i].end.toString(), "vidaDate": dayEvents[i].vidaDate};
timeList.add(timeSlot); timeList.add(timeSlot);
} }
selectedTime = dayEvents[selectedButtonIndex].isoTime!; if (dayEvents.isNotEmpty) {
selectedTime = dayEvents[selectedButtonIndex].isoTime!;
}
setState(() {});
} }
void _onDaySelected(DateTime day) { void _onDaySelected(DateTime day) {
final DateFormat formatter = DateFormat('yyyy-MM-dd'); final DateFormat formatter = DateFormat('yyyy-MM-dd', "en-US");
setState(() { setState(() {
selectedDateDisplay = DateUtil.getMonthDayYearDateFormatted(day); selectedDateDisplay = DateUtil.getMonthDayYearDateFormatted(day);
selectedNextDate = DateUtil.getWeekDayMonthDayYearDateFormatted(day.add(Duration(days: 1)), "en"); selectedNextDate = DateUtil.getWeekDayMonthDayYearDateFormatted(day.add(Duration(days: 1)), "en");

@ -38,7 +38,7 @@ class DoctorCard extends StatelessWidget {
hasShadow: false, hasShadow: false,
), ),
child: Padding( child: Padding(
padding: EdgeInsets.only(top: 14.h,bottom: 20.h), padding: EdgeInsets.only(top: 14.h, bottom: 20.h),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -90,23 +90,21 @@ class DoctorCard extends StatelessWidget {
Row( Row(
children: [ children: [
SizedBox( SizedBox(
width: MediaQuery.of(context).size.width * 0.49, width: MediaQuery.of(context).size.width * 0.55,
child: (isLoading ? "Dr John Smith" : "${doctorsListResponseModel.doctorTitle} ${doctorsListResponseModel.name}") child: (isLoading ? "Dr John Smith" : "${doctorsListResponseModel.doctorTitle} ${doctorsListResponseModel.name}").toString().toText16(isBold: true, maxlines: 1),
.toString()
.toText16(isBold: true, maxlines: 1),
).toShimmer2(isShow: isLoading), ).toShimmer2(isShow: isLoading),
], ],
), ),
SizedBox(height: 2.h), SizedBox(height: 2.h),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
SizedBox( SizedBox(
width: MediaQuery.of(context).size.width * 0.45,
child: (isLoading child: (isLoading
? "Consultant Cardiologist" ? "Consultant Cardiologist"
: doctorsListResponseModel.speciality!.isNotEmpty : doctorsListResponseModel.speciality!.isNotEmpty
? doctorsListResponseModel.speciality!.first ? (doctorsListResponseModel.speciality!.first.length > 32
? '${doctorsListResponseModel.speciality!.first.substring(0, 32)}...'
: doctorsListResponseModel.speciality!.first)
: "") : "")
.toString() .toString()
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 2) .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 2)
@ -121,38 +119,40 @@ class DoctorCard extends StatelessWidget {
).toShimmer2(isShow: isLoading), ).toShimmer2(isShow: isLoading),
], ],
), ),
SizedBox(height: 6.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
labelText: "${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}",
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: "${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}",
).toShimmer2(isShow: isLoading),
bookAppointmentsViewModel.isNearestAppointmentSelected
? doctorsListResponseModel.nearestFreeSlot != null
? AppCustomChipWidget(
// labelText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)),
richText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot))
.toText10(isEnglishOnly: true, color: AppColors.whiteColor),
backgroundColor: AppColors.successColor,
textColor: AppColors.whiteColor,
).toShimmer2(isShow: isLoading)
: SizedBox.shrink()
: SizedBox.shrink(),
],
),
], ],
), ),
), ),
Expanded( Expanded(
flex: 1, flex: 1,
child: Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown) child: Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown).toShimmer2(isShow: isLoading),
.toShimmer2(isShow: isLoading),
), ),
], ],
), ),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
labelText: "${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}",
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: "${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}",
).toShimmer2(isShow: isLoading),
bookAppointmentsViewModel.isNearestAppointmentSelected
? doctorsListResponseModel.nearestFreeSlot != null
? AppCustomChipWidget(
labelText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)),
backgroundColor: AppColors.successColor,
textColor: AppColors.whiteColor,
).toShimmer2(isShow: isLoading)
: SizedBox.shrink()
: SizedBox.shrink(),
],
),
SizedBox(height: 12.h), SizedBox(height: 12.h),
CustomButton( CustomButton(
text: LocaleKeys.bookAppo.tr(context: context), text: LocaleKeys.bookAppo.tr(context: context),

@ -0,0 +1,195 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.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/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:provider/provider.dart';
class DoctorRatingDetails extends StatelessWidget {
const DoctorRatingDetails({super.key});
@override
Widget build(BuildContext context) {
return Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return bookAppointmentsVM.isDoctorRatingDetailsLoading
? Utils.getLoadingWidget()
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
bookAppointmentsVM.doctorsProfileResponseModel.actualDoctorRate!.ceilToDouble().toString().toText44(isBold: true),
SizedBox(height: 4.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"${bookAppointmentsVM.doctorsProfileResponseModel.noOfPatientsRate} ${LocaleKeys.reviews.tr(context: context)}"
.toText16(weight: FontWeight.w500, color: AppColors.greyInfoTextColor),
RatingBar(
initialRating: bookAppointmentsVM.doctorsProfileResponseModel.actualDoctorRate!.toDouble(),
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemSize: 20.h,
ignoreGestures: true,
ratingWidget: RatingWidget(
full: Icon(
Icons.star,
color: AppColors.ratingColorYellow,
size: 24.h,
),
half: Icon(
Icons.star_half,
color: AppColors.ratingColorYellow,
),
empty: Icon(
Icons.star,
color: AppColors.ratingColorYellow,
),
),
tapOnlyMode: true,
unratedColor: Colors.grey[500],
itemPadding: EdgeInsets.symmetric(horizontal: 4.0),
onRatingUpdate: (rating) {
print(rating);
},
),
],
),
SizedBox(height: 8.h),
Container(
margin: EdgeInsets.only(top: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 100.0,
margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0),
child: Text(LocaleKeys.excellent.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))),
getRatingLine(bookAppointmentsVM.doctorDetailsList[0].ratio, Colors.green[700]!),
],
),
Container(
margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
child: Text("${getRatingWidth(bookAppointmentsVM.doctorDetailsList[0].ratio).round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)),
),
],
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 100.0,
margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0),
child: Text(LocaleKeys.vGood.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))),
getRatingLine(bookAppointmentsVM.doctorDetailsList[1].ratio, Color(0xffB7B723)),
],
),
Container(
margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
child: Text("${bookAppointmentsVM.doctorDetailsList[1].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)),
),
],
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 100.0,
margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0),
child: Text(LocaleKeys.good.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))),
getRatingLine(bookAppointmentsVM.doctorDetailsList[2].ratio, Color(0xffEBA727)),
],
),
Container(
margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
child: Text("${bookAppointmentsVM.doctorDetailsList[2].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)),
),
],
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 100.0,
margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0),
child: Text(LocaleKeys.average.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))),
getRatingLine(bookAppointmentsVM.doctorDetailsList[3].ratio, Color(0xffEB7227)),
],
),
Container(
margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
child: Text("${bookAppointmentsVM.doctorDetailsList[3].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)),
),
],
),
),
Container(
margin: EdgeInsets.only(bottom: 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 100.0,
margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0),
child: Text(LocaleKeys.average.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))),
getRatingLine(bookAppointmentsVM.doctorDetailsList[4].ratio, Color(0xffE20C0C)),
],
),
Container(
margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
child: Text("${bookAppointmentsVM.doctorDetailsList[4].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)),
),
],
),
),
],
);
});
}
double getRatingWidth(num patientNumber) {
var width = patientNumber;
return width.roundToDouble();
}
Widget getRatingLine(double patientNumber, Color color) {
return Container(
margin: EdgeInsets.only(top: 10.0),
child: Stack(children: [
SizedBox(
width: 150.0,
height: 7.h,
child: Container(
color: Colors.grey[300],
),
),
SizedBox(
width: patientNumber * 1.55,
height: 7.h,
child: Container(
color: color,
),
),
]),
);
}
}

@ -1,4 +1,5 @@
import 'dart:developer'; import 'dart:developer';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -22,7 +23,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/map/location_map_widget.dart'; import 'package:hmg_patient_app_new/widgets/map/location_map_widget.dart';
import 'package:maps_launcher/maps_launcher.dart'; import 'package:map_launcher/map_launcher.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class CmcSelectionReviewPage extends StatefulWidget { class CmcSelectionReviewPage extends StatefulWidget {
@ -319,16 +320,27 @@ class _CmcSelectionReviewPageState extends State<CmcSelectionReviewPage> {
CmcUiSelectionHelper.showHospitalSelectionBottomSheet(context: context, onHospitalSelected: (hospital) => context.pop()); CmcUiSelectionHelper.showHospitalSelectionBottomSheet(context: context, onHospitalSelected: (hospital) => context.pop());
} }
void _launchDirections(HospitalsModel selectedHospital) { void _launchDirections(HospitalsModel selectedHospital) async {
final double lat = double.parse(selectedHospital.latitude ?? "0.0"); final double lat = double.parse(selectedHospital.latitude ?? "0.0");
final double lng = double.parse(selectedHospital.longitude ?? "0.0"); final double lng = double.parse(selectedHospital.longitude ?? "0.0");
if (lat != 0.0 && lng != 0.0) { if (lat != 0.0 && lng != 0.0) {
MapsLauncher.launchCoordinates( await MapLauncher.showMarker(
lat, mapType: MapType.google,
lng, coords: Coords(lat, lng),
selectedHospital.name ?? "Hospital", title: selectedHospital.name ?? "Hospital",
); ).catchError((err) {
MapLauncher.showMarker(
mapType: Platform.isIOS ? MapType.apple : MapType.google,
coords: Coords(lat, lng),
title: selectedHospital.name ?? "Hospital",
);
});
// MapsLauncher.launchCoordinates(
// lat,
// lng,
// selectedHospital.name ?? "Hospital",
// );
} }
} }

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
@ -10,7 +12,7 @@ import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_h
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:maps_launcher/maps_launcher.dart'; import 'package:map_launcher/map_launcher.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
class FindUsItemCard extends StatelessWidget { class FindUsItemCard extends StatelessWidget {
@ -82,8 +84,18 @@ class FindUsItemCard extends StatelessWidget {
iconColor: AppColors.whiteColor, iconColor: AppColors.whiteColor,
backgroundColor: AppColors.textColor.withValues(alpha: 0.8), backgroundColor: AppColors.textColor.withValues(alpha: 0.8),
textColor: AppColors.whiteColor, textColor: AppColors.whiteColor,
onChipTap: () { onChipTap: () async {
MapsLauncher.launchCoordinates(double.parse(getHMGLocationsModel.latitude ?? "0.0"), double.parse(getHMGLocationsModel.longitude ?? "0.0"), getHMGLocationsModel.locationName!); await MapLauncher.showMarker(
mapType: MapType.google,
coords: Coords(double.parse(getHMGLocationsModel.latitude ?? "0.0"), double.parse(getHMGLocationsModel.longitude ?? "0.0")),
title: getHMGLocationsModel.locationName ?? "Hospital",
).catchError((err) {
MapLauncher.showMarker(
mapType: Platform.isIOS ? MapType.apple : MapType.google,
coords: Coords(double.parse(getHMGLocationsModel.latitude ?? "0.0"), double.parse(getHMGLocationsModel.longitude ?? "0.0")),
title: getHMGLocationsModel.locationName ?? "Hospital",
);
});
}, },
), ),
SizedBox(width: 4.w), SizedBox(width: 4.w),

@ -140,6 +140,7 @@ class PatientInformationStepState extends State<PatientInformationStep> {
controller: _phoneController, controller: _phoneController,
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
fontFamily: "Poppins",
onChange: (value) { onChange: (value) {
formManager.updatePatientPhone(value ?? ''); formManager.updatePatientPhone(value ?? '');
}, },

@ -73,6 +73,7 @@ class SearchEReferralFormFormState extends State<SearchEReferralFormForm> {
hintText: formManager.searchCriteria == 0 ? "Enter Identification Number" : "Enter Referral Number", hintText: formManager.searchCriteria == 0 ? "Enter Identification Number" : "Enter Referral Number",
labelText: formManager.searchCriteria == 0 ? "Identification Number" : "Referral Number", labelText: formManager.searchCriteria == 0 ? "Identification Number" : "Referral Number",
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
fontFamily: "Poppins",
errorMessage: formManager.errors.searchValue, errorMessage: formManager.errors.searchValue,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.searchValue), hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.searchValue),
onChange: (value) { onChange: (value) {
@ -94,6 +95,7 @@ class SearchEReferralFormFormState extends State<SearchEReferralFormForm> {
controller: _phoneController, controller: _phoneController,
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
fontFamily: "Poppins",
onChange: (value) { onChange: (value) {
formManager.updateSearchPhone(value ?? ''); formManager.updateSearchPhone(value ?? '');
// _validateForm(formManager); // _validateForm(formManager);

@ -526,8 +526,8 @@ class CallAmbulancePage extends StatelessWidget {
textPlaceInput(context) { textPlaceInput(context) {
return Consumer<LocationViewModel>(builder: (_, vm, __) { return Consumer<LocationViewModel>(builder: (_, vm, __) {
print("the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}"); // print("the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}");
return SizedBox( return (vm.geocodeResponse != null && vm.geocodeResponse!.results.isNotEmpty) ? SizedBox(
width: MediaQuery.sizeOf(context).width, width: MediaQuery.sizeOf(context).width,
child: TextInputWidget( child: TextInputWidget(
labelText: LocaleKeys.enterPickupLocationManually.tr(context: context), labelText: LocaleKeys.enterPickupLocationManually.tr(context: context),
@ -549,7 +549,7 @@ class CallAmbulancePage extends StatelessWidget {
).onPress(() { ).onPress(() {
openLocationInputBottomSheet(context); openLocationInputBottomSheet(context);
}), }),
); ) : SizedBox.shrink();
}); });
} }

@ -159,6 +159,15 @@ class ErOnlineCheckinHome extends StatelessWidget {
await vm.getPatientERPaymentInformation(onSuccess: (response) { await vm.getPatientERPaymentInformation(onSuccess: (response) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
vm.navigateToEROnlineCheckInPaymentPage(); vm.navigateToEROnlineCheckInPaymentPage();
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}); });
}, },
onHospitalSearch: (value) { onHospitalSearch: (value) {

@ -62,25 +62,22 @@ class ErOnlineCheckinPaymentDetailsPage extends StatelessWidget {
SizedBox(height: 12.h), SizedBox(height: 12.h),
Wrap( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
spacing: 6.w, spacing: 4.w,
runSpacing: 6.h, runSpacing: 6.h,
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
labelText: "File No.: ${appState.getAuthenticatedUser()!.patientId!.toString()}", labelText: "${LocaleKeys.fileno.tr(context: context)}.: ${appState.getAuthenticatedUser()!.patientId!.toString()}",
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
), ),
AppCustomChipWidget( AppCustomChipWidget(
labelText: LocaleKeys.erClinic.tr(), labelText: LocaleKeys.erClinic.tr(),
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
), ),
AppCustomChipWidget( AppCustomChipWidget(
labelText: emergencyServicesViewModel.selectedHospital!.name, labelText: emergencyServicesViewModel.selectedHospital!.name,
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
), ),
AppCustomChipWidget( AppCustomChipWidget(
icon: AppAssets.calendar, icon: AppAssets.calendar,
labelText: DateUtil.formatDateToDate(DateTime.now(), false), labelText: DateUtil.formatDateToDate(DateTime.now(), false),
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w), labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 8.w),
), ),
], ],
), ),

@ -337,8 +337,6 @@ class _ErOnlineCheckinPaymentPageState extends State<ErOnlineCheckinPaymentPage>
} }
startApplePay() async { startApplePay() async {
// showCommonBottomSheet(context,
// child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false);
LoaderBottomSheet.showLoader(); LoaderBottomSheet.showLoader();
transID = Utils.getAdvancePaymentTransID( transID = Utils.getAdvancePaymentTransID(
emergencyServicesViewModel.selectedHospital!.iD, emergencyServicesViewModel.selectedHospital!.iD,
@ -405,8 +403,8 @@ class _ErOnlineCheckinPaymentPageState extends State<ErOnlineCheckinPaymentPage>
applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest, applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest,
currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
onFailed: (failureResult) async { onFailed: (failureResult) async {
LoaderBottomSheet.hideLoader();
log("failureResult: ${failureResult.message.toString()}"); log("failureResult: ${failureResult.message.toString()}");
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: failureResult.message.toString()), child: Utils.getErrorWidget(loadingText: failureResult.message.toString()),
@ -416,6 +414,7 @@ class _ErOnlineCheckinPaymentPageState extends State<ErOnlineCheckinPaymentPage>
); );
}, },
onSucceeded: (successResult) async { onSucceeded: (successResult) async {
LoaderBottomSheet.hideLoader();
log("successResult: ${successResult.responseMessage.toString()}"); log("successResult: ${successResult.responseMessage.toString()}");
selectedPaymentMethod = successResult.paymentOption ?? "VISA"; selectedPaymentMethod = successResult.paymentOption ?? "VISA";
checkPaymentStatus(); checkPaymentStatus();

@ -36,6 +36,7 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
late HabibWalletViewModel habibWalletVM; late HabibWalletViewModel habibWalletVM;
late AppState appState; late AppState appState;
final TextEditingController amountTextController = TextEditingController(); final TextEditingController amountTextController = TextEditingController();
final TextEditingController notesTextController = TextEditingController();
@override @override
void initState() { void initState() {
@ -97,11 +98,12 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
isBorderAllowed: false, isBorderAllowed: false,
isAllowLeadingIcon: true, isAllowLeadingIcon: true,
autoFocus: true, autoFocus: true,
fontSize: 40, fontSize: 40.f,
padding: EdgeInsets.symmetric(horizontal: 8.h, vertical: 0.h), padding: EdgeInsets.symmetric(horizontal: 8.h, vertical: 0.h),
focusNode: textFocusNode, focusNode: textFocusNode,
isWalletAmountInput: true, isWalletAmountInput: true,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true), keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
fontFamily: "Poppins",
// leadingIcon: AppAssets.student_card, // leadingIcon: AppAssets.student_card,
), ),
), ),
@ -209,23 +211,19 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
SizedBox(height: 16.h), SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Row( TextInputWidget(
mainAxisAlignment: MainAxisAlignment.spaceBetween, labelText: LocaleKeys.notes.tr(context: context),
children: [ hintText: "",
Row( controller: notesTextController,
children: [ keyboardType: TextInputType.text,
Utils.buildSvgWithAssets(icon: AppAssets.notes_icon, width: 40.h, height: 40.h), isEnable: true,
SizedBox(width: 8.h), prefix: null,
Column( autoFocus: false,
crossAxisAlignment: CrossAxisAlignment.start, isAllowRadius: true,
children: [ isBorderAllowed: false,
LocaleKeys.notes.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), isAllowLeadingIcon: true,
"Lorem Ipsum".toText14(color: AppColors.textColor, weight: FontWeight.w500, letterSpacing: -0.2), leadingIcon: AppAssets.notes_icon,
], hasError: false,
),
],
),
],
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
], ],
@ -271,6 +269,7 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
); );
} else { } else {
habibWalletVM.setWalletRechargeAmount(num.parse(amountTextController.text)); habibWalletVM.setWalletRechargeAmount(num.parse(amountTextController.text));
habibWalletVM.setNotesText(notesTextController.text);
// habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId.toString(), "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}", // habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId.toString(), "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}",
// appState.getAuthenticatedUser()!.mobileNumber!); // appState.getAuthenticatedUser()!.mobileNumber!);
Navigator.of(context).push( Navigator.of(context).push(

@ -316,6 +316,8 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
context, context,
child: Utils.getSuccessWidget(loadingText: "Payment Successful!"), child: Utils.getSuccessWidget(loadingText: "Payment Successful!"),
callBackFunc: () { callBackFunc: () {
habibWalletVM.initHabibWalletProvider();
habibWalletVM.getPatientBalanceAmount();
Navigator.of(context).pop(); Navigator.of(context).pop();
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },

@ -28,6 +28,8 @@ 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/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -145,6 +147,10 @@ class _MultiPageBottomSheetState extends State<MultiPageBottomSheet> {
], ],
).paddingAll(16.h), ).paddingAll(16.h),
).onPress(() { ).onPress(() {
habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId!.toString(),
"${appState.getAuthenticatedUser()!.firstName.toString()} ${appState.getAuthenticatedUser()!.lastName.toString()}", appState.getAuthenticatedUser()!.mobileNumber!.toString());
habibWalletVM.setCurrentIndex(0);
habibWalletVM.setSelectedRechargeType(1);
Navigator.of(context).pop(); Navigator.of(context).pop();
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -167,7 +173,24 @@ class _MultiPageBottomSheetState extends State<MultiPageBottomSheet> {
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h), Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
], ],
).paddingAll(16.h), ).paddingAll(16.h),
), ).onPress(() {
DialogService dialogService = getIt.get<DialogService>();
dialogService.showFamilyBottomSheetWithoutH(
label: LocaleKeys.familyTitle.tr(context: context),
message: "",
isShowManageButton: false,
isForWalletRecharge: true,
onSwitchPress: (FamilyFileResponseModelLists profile) {
habibWalletVM.setDepositorDetails(profile.responseId.toString(), profile.patientName.toString(), profile.mobileNumber.toString());
habibWalletVM.setCurrentIndex(0);
habibWalletVM.setSelectedRechargeType(2);
Navigator.of(context).pop();
Navigator.of(context).pop();
// medicalFileViewModel.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber);
},
profiles: getIt.get<MedicalFileViewModel>().patientFamilyFiles);
}),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Container( Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(

@ -204,7 +204,7 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
crossAxisCount: 3, // 4 icons per row crossAxisCount: 3, // 4 icons per row
crossAxisSpacing: 16.w, crossAxisSpacing: 16.w,
mainAxisSpacing: 16.w, mainAxisSpacing: 16.w,
childAspectRatio: 0.80), childAspectRatio: 0.85),
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,
itemCount: type == HealthCalculatorEnum.general ? generalHealthServices.length : womenHealthServices.length, itemCount: type == HealthCalculatorEnum.general ? generalHealthServices.length : womenHealthServices.length,
@ -214,6 +214,7 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
icon: type == HealthCalculatorEnum.general ? generalHealthServices[index].icon : womenHealthServices[index].icon, icon: type == HealthCalculatorEnum.general ? generalHealthServices[index].icon : womenHealthServices[index].icon,
labelText: type == HealthCalculatorEnum.general ? generalHealthServices[index].title : womenHealthServices[index].title, labelText: type == HealthCalculatorEnum.general ? generalHealthServices[index].title : womenHealthServices[index].title,
onTap: () { onTap: () {
Navigator.pop(context);
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: HealthCalculatorDetailedPage( page: HealthCalculatorDetailedPage(

@ -29,6 +29,7 @@ import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dar
import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/parking/paking_page.dart'; import 'package:hmg_patient_app_new/presentation/parking/paking_page.dart';
import 'package:hmg_patient_app_new/presentation/servicesPriceList/services_price_list_page.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -150,7 +151,7 @@ class ServicesPage extends StatelessWidget {
}), }),
HmgServicesComponentModel( HmgServicesComponentModel(
3, 3,
"Blood Donation".needTranslation, LocaleKeys.bloodDonation.tr(context: getIt.get<NavigationService>().navigatorKey.currentContext!),
"".needTranslation, "".needTranslation,
AppAssets.blood_donation_icon, AppAssets.blood_donation_icon,
bgColor: AppColors.bloodDonationCardColor, bgColor: AppColors.bloodDonationCardColor,
@ -158,14 +159,28 @@ class ServicesPage extends StatelessWidget {
route: AppRoutes.bloodDonationPage, route: AppRoutes.bloodDonationPage,
), ),
HmgServicesComponentModel( HmgServicesComponentModel(
3, 113,
"My Child Vaccine".needTranslation, LocaleKeys.servicePriceList.tr(context: getIt.get<NavigationService>().navigatorKey.currentContext!),
"".needTranslation, "".needTranslation,
AppAssets.my_child_vaccine_icon, AppAssets.saudi_riyal_icon,
bgColor: AppColors.myChildVaccineCardColor, bgColor: AppColors.textColor,
true, false,
route: AppRoutes.myChildVaccine, route: null,
), onTap: () {
Navigator.of(getIt.get<NavigationService>().navigatorKey.currentContext!).push(
CustomPageRoute(page: ServicesPriceListPage()),
);
},
)
// HmgServicesComponentModel(
// 3,
// "My Child Vaccine".needTranslation,
// "".needTranslation,
// AppAssets.my_child_vaccine_icon,
// bgColor: AppColors.myChildVaccineCardColor,
// true,
// route: AppRoutes.myChildVaccine,
// ),
// HmgServicesComponentModel( // HmgServicesComponentModel(
// 11, // 11,
// "Covid 19 Test".needTranslation, // "Covid 19 Test".needTranslation,
@ -302,6 +317,9 @@ class ServicesPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
bloodDonationViewModel = Provider.of<BloodDonationViewModel>(context); bloodDonationViewModel = Provider.of<BloodDonationViewModel>(context);
medicalFileViewModel = Provider.of<MedicalFileViewModel>(context); medicalFileViewModel = Provider.of<MedicalFileViewModel>(context);
hmgServices.removeWhere((element) => Utils.havePrivilege(element.action) == false);
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView( body: CollapsingListView(
@ -382,7 +400,7 @@ class ServicesPage extends StatelessWidget {
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h), Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h),
LocaleKeys.habibWallet.tr().toText14(weight: FontWeight.w600, maxlines: 2).expanded, LocaleKeys.habibWallet.tr().toText14(weight: FontWeight.w600, maxlines: 2).expanded,
Utils.buildSvgWithAssets(icon: AppAssets.arrow_forward), Utils.buildSvgWithAssets(icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward),
], ],
), ),
Spacer(), Spacer(),
@ -442,7 +460,7 @@ class ServicesPage extends StatelessWidget {
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.services_medical_file_icon, width: 40.w, height: 40.h), Utils.buildSvgWithAssets(icon: AppAssets.services_medical_file_icon, width: 40.w, height: 40.h),
LocaleKeys.medicalFile.tr().toText16(weight: FontWeight.w600, maxlines: 2).expanded, LocaleKeys.medicalFile.tr().toText16(weight: FontWeight.w600, maxlines: 2).expanded,
Utils.buildSvgWithAssets(icon: AppAssets.arrow_forward), Utils.buildSvgWithAssets(icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward),
], ],
), ),
Spacer(), Spacer(),
@ -667,7 +685,7 @@ class ServicesPage extends StatelessWidget {
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
LocaleKeys.hmgContact.tr().toText14(weight: FontWeight.w500) Expanded(child: LocaleKeys.hmgContact.tr().toText14(weight: FontWeight.w500))
], ],
), ),
), ),

@ -652,7 +652,7 @@ class _LandingPageState extends State<LandingPage> {
], ],
).paddingSymmetrical(24.w, 0.h), ).paddingSymmetrical(24.w, 0.h),
SizedBox( SizedBox(
height: 411.h, height: 431.h,
child: ListView.separated( child: ListView.separated(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: LandingPageData.getServiceCardsList.length, itemCount: LandingPageData.getServiceCardsList.length,

@ -87,7 +87,7 @@ class HabibWalletCard extends StatelessWidget {
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
SizedBox(width: 8.h), SizedBox(width: 8.h),
habibWalletVM.habibWalletAmount.toString().toText32(isBold: true).toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 40.h), habibWalletVM.habibWalletAmount.toString().toText32(isBold: true, isEnglishOnly: true).toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 40.h),
], ],
); );
}), }),

@ -65,11 +65,11 @@ class SmallServiceCard extends StatelessWidget {
Utils.buildSvgWithAssets(icon: icon, iconColor: iconColor, width: 32.h, height: 32.h), Utils.buildSvgWithAssets(icon: icon, iconColor: iconColor, width: 32.h, height: 32.h),
SizedBox(height: 6.h), SizedBox(height: 6.h),
if (isFoldable || isTablet) ...[ if (isFoldable || isTablet) ...[
title.tr(context: context).toText11(color: textColor, weight: FontWeight.w500, isCenter: true, letterSpacing: -0.2), title.tr(context: context).toText10(color: textColor, weight: FontWeight.w500, isCenter: true, letterSpacing: -0.2, maxlines: 1),
subtitle.tr(context: context).toText11(color: textColor, weight: FontWeight.w500, isCenter: true, letterSpacing: -0.2), subtitle.tr(context: context).toText10(color: textColor, weight: FontWeight.w500, isCenter: true, letterSpacing: -0.2, maxlines: 1),
] else ...[ ] else ...[
title.tr(context: context).toText12(color: textColor, fontWeight: FontWeight.w500, isCenter: true, letterSpacing: -0.2), title.tr(context: context).toText12(color: textColor, fontWeight: FontWeight.w500, isCenter: true, letterSpacing: -0.2, maxLine: 1),
subtitle.tr(context: context).toText12(color: textColor, fontWeight: FontWeight.w500, isCenter: true, letterSpacing: -0.2), subtitle.tr(context: context).toText12(color: textColor, fontWeight: FontWeight.w500, isCenter: true, letterSpacing: -0.2, maxLine: 1),
] ]
], ],
), ),

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/app_state.dart';
@ -19,7 +21,7 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/map/location_map_widget.dart'; import 'package:hmg_patient_app_new/widgets/map/location_map_widget.dart';
import 'package:maps_launcher/maps_launcher.dart'; import 'package:map_launcher/map_launcher.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class HhcSelectionReviewPage extends StatefulWidget { class HhcSelectionReviewPage extends StatefulWidget {
@ -142,9 +144,19 @@ class _HhcSelectionReviewPageState extends State<HhcSelectionReviewPage> {
); );
} }
void _launchDirectionsToLocation(double lat, double lng, String locationName) { void _launchDirectionsToLocation(double lat, double lng, String locationName) async {
if (lat != 0.0 && lng != 0.0) { if (lat != 0.0 && lng != 0.0) {
MapsLauncher.launchCoordinates(lat, lng, locationName); await MapLauncher.showMarker(
mapType: MapType.google,
coords: Coords(lat, lng),
title: locationName,
).catchError((err) {
MapLauncher.showMarker(
mapType: Platform.isIOS ? MapType.apple : MapType.google,
coords: Coords(lat, lng),
title: locationName,
);
});
} }
} }

@ -1,6 +1,8 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/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/string_extensions.dart';
@ -11,16 +13,20 @@ import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class PatientInsuranceCardUpdateCard extends StatelessWidget { class PatientInsuranceCardUpdateCard extends StatelessWidget {
PatientInsuranceCardUpdateCard({super.key}); PatientInsuranceCardUpdateCard({super.key});
late InsuranceViewModel insuranceViewModel; late InsuranceViewModel insuranceViewModel;
late AppState appState;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
insuranceViewModel = Provider.of<InsuranceViewModel>(context); insuranceViewModel = Provider.of<InsuranceViewModel>(context);
appState = getIt.get<AppState>();
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@ -49,7 +55,7 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Haroon Amjad".toText16(weight: FontWeight.w600), insuranceViewModel.patientInsuranceUpdateResponseModel!.memberName!.toText16(weight: FontWeight.w600),
"Policy: ${insuranceViewModel.patientInsuranceUpdateResponseModel!.policyNumber}".toText12(isBold: true, color: AppColors.lightGrayColor), "Policy: ${insuranceViewModel.patientInsuranceUpdateResponseModel!.policyNumber}".toText12(isBold: true, color: AppColors.lightGrayColor),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Row( Row(
@ -99,7 +105,41 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget {
iconColor: AppColors.whiteColor, iconColor: AppColors.whiteColor,
iconSize: 20.w, iconSize: 20.w,
text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}", text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}",
onPressed: () {}, onPressed: () {
LoaderBottomSheet.showLoader();
insuranceViewModel.updatePatientInsuranceCard(
patientID: appState.getAuthenticatedUser()!.patientId!,
patientType: appState.getAuthenticatedUser()!.patientType!,
patientIdentificationID: appState.getAuthenticatedUser()!.patientIdentificationNo!,
mobileNo: appState.getAuthenticatedUser()!.mobileNumber!,
insuranceCardImage: "",
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.success.tr(context: context),
context,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr(context: context)),
callBackFunc: () {
Navigator.pop(context);
},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getErrorWidget(loadingText: err.toString()),
callBackFunc: () {
Navigator.pop(context);
},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
backgroundColor: insuranceViewModel.patientInsuranceUpdateResponseModel != null ? AppColors.successColor : AppColors.lightGrayBGColor, backgroundColor: insuranceViewModel.patientInsuranceUpdateResponseModel != null ? AppColors.successColor : AppColors.lightGrayBGColor,
borderColor: AppColors.successColor.withOpacity(0.01), borderColor: AppColors.successColor.withOpacity(0.01),
textColor: AppColors.whiteColor, textColor: AppColors.whiteColor,

@ -75,10 +75,11 @@ class PatientInsuranceCard extends StatelessWidget {
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon, icon: AppAssets.doctor_calendar_icon,
labelText: "${LocaleKeys.expiryDate.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceCardDetailsModel.cardValidTo), false)}", // labelText: "${LocaleKeys.expiryDate.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceCardDetailsModel.cardValidTo), false)}",
richText: "${LocaleKeys.expiryDate.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceCardDetailsModel.cardValidTo), false)}".toText10(isEnglishOnly: true),
labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h), labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h),
), ),
AppCustomChipWidget(labelText: LocaleKeys.patientCardID.tr(namedArgs: {'id': insuranceCardDetailsModel.patientCardID ?? ''}, context: context)), AppCustomChipWidget(richText: LocaleKeys.patientCardID.tr(namedArgs: {'id': insuranceCardDetailsModel.patientCardID ?? ''}, context: context).toText10(isEnglishOnly: true)),
], ],
), ),
SizedBox(height: 10.h), SizedBox(height: 10.h),

@ -203,7 +203,6 @@ class _AlphabetScrollPageState extends State<AlphabeticScroll> {
verticalOffset: 100.0, verticalOffset: 100.0,
child: FadeInAnimation( child: FadeInAnimation(
child: LabOrderByTest( child: LabOrderByTest(
appState: getIt<AppState>(), appState: getIt<AppState>(),
onTap: () { onTap: () {
if (items[itemIndex].model != null) { if (items[itemIndex].model != null) {

@ -250,7 +250,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
icon: AppAssets.file_icon, icon: AppAssets.file_icon,
labelText: "${LocaleKeys.fileno.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}", richText: "${LocaleKeys.fileno.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}".toText10(isEnglishOnly: true),
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w),
), ),
AppCustomChipWidget( AppCustomChipWidget(
@ -1579,6 +1579,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
weight: FontWeight.w600, weight: FontWeight.w600,
), ),
), ),
Utils.buildSvgWithAssets(icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward, width: 18.w, height: 18.h),
], ],
), ),
SizedBox(height: 14.h), SizedBox(height: 14.h),
@ -1601,6 +1602,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
child: value.toText17( child: value.toText17(
isBold: true, isBold: true,
color: AppColors.textColor, color: AppColors.textColor,
isEnglishOnly: true
), ),
), ),
if (unit.isNotEmpty) ...[ if (unit.isNotEmpty) ...[

@ -17,6 +17,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:open_filex/open_filex.dart'; import 'package:open_filex/open_filex.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -197,49 +198,23 @@ class _PatientSickleavesListPageState extends State<PatientSickleavesListPage> {
], ],
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Row( Wrap(
direction: Axis.horizontal,
spacing: 6.h,
runSpacing: 6.h,
children: [ children: [
CustomButton( AppCustomChipWidget(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(sickLeave.appointmentDate), false), labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(sickLeave.appointmentDate), false),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
), ),
SizedBox(width: 8.h), AppCustomChipWidget(
CustomButton( labelText: model.isSickLeavesSortByClinic ? sickLeave.projectName! : sickLeave.clinicName!,
text: model.isSickLeavesSortByClinic ? sickLeave.projectName! : sickLeave.clinicName!,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
), ),
SizedBox(width: 8.h), AppCustomChipWidget(
CustomButton( labelText: "${sickLeave.sickLeaveDays} Days",
text: "${sickLeave.sickLeaveDays} Days",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
), ),
], ],
), ),
SizedBox(height: 8.h), SizedBox(height: 12.h),
Row( Row(
children: [ children: [
Expanded( Expanded(

@ -18,6 +18,8 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'dart:ui' as ui;
class MedicalFileAppointmentCard extends StatelessWidget { class MedicalFileAppointmentCard extends StatelessWidget {
final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
final MyAppointmentsViewModel myAppointmentsViewModel; final MyAppointmentsViewModel myAppointmentsViewModel;
@ -39,11 +41,12 @@ class MedicalFileAppointmentCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
richText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false) richText: Directionality(
.toText12( textDirection: ui.TextDirection.ltr,
color: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor, child: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)
fontWeight: FontWeight.w500) .toText12(color: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor, fontWeight: FontWeight.w500, isEnglishOnly: true)
.paddingOnly(left: 8.w), .paddingSymmetrical(8.w, 0),
),
icon: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppAssets.appointment_calendar_icon : AppAssets.alarm_clock_icon, icon: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppAssets.appointment_calendar_icon : AppAssets.alarm_clock_icon,
iconColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor, iconColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor,
iconSize: 16.w, iconSize: 16.w,
@ -71,9 +74,7 @@ class MedicalFileAppointmentCard extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
(patientAppointmentHistoryResponseModel.doctorNameObj ?? "") (patientAppointmentHistoryResponseModel.doctorNameObj ?? "").toText14(isBold: true, maxlines: 1).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading),
.toText14(isBold: true, maxlines: 1)
.toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading),
(patientAppointmentHistoryResponseModel.clinicName ?? "") (patientAppointmentHistoryResponseModel.clinicName ?? "")
.toText12(maxLine: 1, fontWeight: FontWeight.w500, color: AppColors.greyTextColor) .toText12(maxLine: 1, fontWeight: FontWeight.w500, color: AppColors.greyTextColor)
.toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading), .toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading),
@ -103,10 +104,8 @@ class MedicalFileAppointmentCard extends StatelessWidget {
// widget.myAppointmentsViewModel.getPatientAppointments(true, false); // widget.myAppointmentsViewModel.getPatientAppointments(true, false);
}); });
}, },
backgroundColor: backgroundColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.15),
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.15), borderColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
borderColor:
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction), textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14.f, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,

@ -80,7 +80,10 @@ class MonthlyReport extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
"${appState.getAuthenticatedUser()!.emailAddress}".toText16(color: AppColors.textColor, weight: FontWeight.w500), SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: "${appState.getAuthenticatedUser()!.emailAddress}".toText16(color: AppColors.textColor, weight: FontWeight.w500, maxlines: 2),
),
], ],
), ),
], ],

@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/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/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart';
@ -25,6 +26,7 @@ class FamilyCards extends StatefulWidget {
final bool isRequestDesign; final bool isRequestDesign;
final bool isLeftAligned; final bool isLeftAligned;
final bool isShowRemoveButton; final bool isShowRemoveButton;
final bool isForWalletRecharge;
const FamilyCards( const FamilyCards(
{super.key, {super.key,
@ -35,6 +37,7 @@ class FamilyCards extends StatefulWidget {
this.isBottomSheet = false, this.isBottomSheet = false,
this.isRequestDesign = false, this.isRequestDesign = false,
this.isLeftAligned = false, this.isLeftAligned = false,
this.isForWalletRecharge = false,
this.isShowRemoveButton = false}); this.isShowRemoveButton = false});
@override @override
@ -206,7 +209,21 @@ class _FamilyCardsState extends State<FamilyCards> {
height: 4.h, height: 4.h,
), ),
Spacer(), Spacer(),
CustomButton( widget.isForWalletRecharge ? CustomButton(
height: 40.h,
onPressed: () {
widget.onSelect(profile);
// if (canSwitch) widget.onSelect(profile);
},
text: LocaleKeys.select.tr(context: context),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 13.h,
icon: AppAssets.activeCheck,
iconColor: isActive || !canSwitch ? (isActive ? null : AppColors.greyTextColor) : AppColors.primaryRedColor,
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0),
).paddingOnly(top: 0, bottom: 0) : CustomButton(
height: 40.h, height: 40.h,
onPressed: () { onPressed: () {
if (canSwitch) widget.onSelect(profile); if (canSwitch) widget.onSelect(profile);

@ -20,26 +20,18 @@ class NotificationDetailsPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Debug logging
print('=== Notification Details ===');
print('Message: ${notification.message}');
print('MessageType: ${notification.messageType}');
print('MessageTypeData: ${notification.messageTypeData}');
print('VideoURL: ${notification.videoURL}');
print('========================');
return CollapsingListView( return CollapsingListView(
title: LocaleKeys.notificationDetails.tr(), title: LocaleKeys.notificationDetails.tr(),
trailing: IconButton( // trailing: IconButton(
icon: Icon( // icon: Icon(
Icons.share_outlined, // Icons.share_outlined,
size: 24.h, // size: 24.h,
color: AppColors.textColor, // color: AppColors.textColor,
), // ),
onPressed: () { // onPressed: () {
_shareNotification(); // _shareNotification();
}, // },
), // ),
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -230,21 +222,21 @@ class NotificationDetailsPage extends StatelessWidget {
), ),
// Additional notification info // Additional notification info
if (notification.notificationType != null && notification.notificationType!.isNotEmpty) // if (notification.notificationType != null && notification.notificationType!.isNotEmpty)
Column( // Column(
crossAxisAlignment: CrossAxisAlignment.start, // crossAxisAlignment: CrossAxisAlignment.start,
children: [ // children: [
LocaleKeys.typeNotification.tr().toText14( // LocaleKeys.typeNotification.tr().toText14(
weight: FontWeight.w600, // weight: FontWeight.w600,
color: AppColors.greyTextColor, // color: AppColors.greyTextColor,
), // ),
SizedBox(height: 8.h), // SizedBox(height: 8.h),
notification.notificationType!.toText16( // notification.notificationType!.toText16(
weight: FontWeight.w400, // weight: FontWeight.w400,
color: AppColors.textColor, // color: AppColors.textColor,
), // ),
], // ],
), // ),
], ],
), ),
); );

@ -23,7 +23,7 @@ class NotificationsListPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CollapsingListView( return CollapsingListView(
title: LocaleKeys.notifications.tr(context: context), title: LocaleKeys.notification.tr(context: context),
child: SingleChildScrollView( child: SingleChildScrollView(
child: Consumer<NotificationsViewModel>(builder: (context, notificationsVM, child) { child: Consumer<NotificationsViewModel>(builder: (context, notificationsVM, child) {
return Container( return Container(

@ -1,4 +1,4 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -7,12 +7,12 @@ 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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/qr_parking/models/qr_parking_response_model.dart'; import 'package:hmg_patient_app_new/features/qr_parking/models/qr_parking_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:map_launcher/map_launcher.dart';
import '../../features/qr_parking/qr_parking_view_model.dart'; import '../../features/qr_parking/qr_parking_view_model.dart';
import '../../theme/colors.dart'; import '../../theme/colors.dart';
import '../../widgets/appbar/app_bar_widget.dart'; import '../../widgets/appbar/app_bar_widget.dart';
import '../../widgets/buttons/custom_button.dart'; import '../../widgets/buttons/custom_button.dart';
import '../../widgets/chip/app_custom_chip_widget.dart'; import '../../widgets/chip/app_custom_chip_widget.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../widgets/routes/custom_page_route.dart'; import '../../widgets/routes/custom_page_route.dart';
@ -29,7 +29,7 @@ class ParkingSlot extends StatefulWidget {
} }
class _ParkingSlotState extends State<ParkingSlot> { class _ParkingSlotState extends State<ParkingSlot> {
void _openDirection() { void _openDirection() async {
final lat = widget.model.latitude; final lat = widget.model.latitude;
final lng = widget.model.longitude; final lng = widget.model.longitude;
@ -48,7 +48,17 @@ class _ParkingSlotState extends State<ParkingSlot> {
return; return;
} }
MapsLauncher.launchCoordinates(lat, lng); await MapLauncher.showMarker(
mapType: MapType.google,
coords: Coords(lat, lng),
title: "Parking Location",
).catchError((err) {
MapLauncher.showMarker(
mapType: Platform.isIOS ? MapType.apple : MapType.google,
coords: Coords(lat, lng),
title: "Parking Location",
);
});
} }

@ -139,7 +139,7 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
labelText: LocaleKeys.ratingValue.tr(namedArgs: {'rating': widget.prescriptionsResponseModel.decimalDoctorRate.toString()}, context: context), labelText: LocaleKeys.ratingValue.tr(namedArgs: {'rating': widget.prescriptionsResponseModel.decimalDoctorRate.toString()}, context: context),
), ),
AppCustomChipWidget( AppCustomChipWidget(
labelText: widget.prescriptionsResponseModel.name!, labelText: widget.prescriptionsResponseModel.name ?? "",
), ),
], ],
), ),
@ -148,7 +148,7 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
text: LocaleKeys.downloadPrescription.tr(context: context), text: LocaleKeys.downloadPrescription.tr(context: context),
onPressed: () async { onPressed: () async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPrescriptionPDFPleaseWait.tr(context: context)); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPrescriptionPDFPleaseWait.tr(context: context));
await prescriptionVM.getPrescriptionPDFBase64(widget.prescriptionsResponseModel).then((val) async { await prescriptionVM.getPrescriptionPDFBase64(widget.prescriptionsResponseModel, onSuccess: (value) async {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
if (prescriptionVM.prescriptionPDFBase64Data.isNotEmpty) { if (prescriptionVM.prescriptionPDFBase64Data.isNotEmpty) {
String path = await Utils.createFileFromString(prescriptionVM.prescriptionPDFBase64Data, "pdf"); String path = await Utils.createFileFromString(prescriptionVM.prescriptionPDFBase64Data, "pdf");
@ -164,6 +164,15 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
); );
} }
} }
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}); });
}, },
backgroundColor: AppColors.successColor.withValues(alpha: 0.15), backgroundColor: AppColors.successColor.withValues(alpha: 0.15),

@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_mo
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
@ -56,74 +57,18 @@ class PrescriptionItemView extends StatelessWidget {
spacing: 6.h, spacing: 6.h,
runSpacing: 6.h, runSpacing: 6.h,
children: [ children: [
Row( AppCustomChipWidget(
mainAxisSize: MainAxisSize.min, labelText: "${LocaleKeys.route.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].route}",
children: [ ).toShimmer2(isShow: isLoading),
CustomButton( AppCustomChipWidget(
text: "${LocaleKeys.route.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].route}", labelText: "${LocaleKeys.frequency.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].frequency}",
onPressed: () {}, ).toShimmer2(isShow: isLoading),
backgroundColor: AppColors.greyColor, AppCustomChipWidget(
borderColor: AppColors.greyColor, labelText: "${LocaleKeys.dailyDoses.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].doseDailyQuantity}",
textColor: AppColors.blackColor, ).toShimmer2(isShow: isLoading),
fontSize: 10, AppCustomChipWidget(
fontWeight: FontWeight.w500, labelText: "${LocaleKeys.days.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].days}",
borderRadius: 8, ).toShimmer2(isShow: isLoading),
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
).toShimmer2(isShow: isLoading),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
text: "${LocaleKeys.frequency.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].frequency}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
).toShimmer2(isShow: isLoading),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
text: "${LocaleKeys.dailyDoses.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].doseDailyQuantity}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
).toShimmer2(isShow: isLoading),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
text: "${LocaleKeys.days.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].days}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
).toShimmer2(isShow: isLoading),
],
),
], ],
).paddingSymmetrical(16.h, 0.h), ).paddingSymmetrical(16.h, 0.h),
SizedBox(height: 8.h), SizedBox(height: 8.h),

@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -164,7 +165,7 @@ class _PrescriptionsListPageState extends State<PrescriptionsListPage> {
], ],
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
model.patientPrescriptionOrdersViewList[index].filterName!.toText16(isBold: true) (model.patientPrescriptionOrdersViewList[index].filterName ?? "").toText16(isBold: true)
], ],
), ),
), ),
@ -207,32 +208,16 @@ class _PrescriptionsListPageState extends State<PrescriptionsListPage> {
], ],
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Row( Wrap(
direction: Axis.horizontal,
spacing: 6.h,
runSpacing: 6.h,
children: [ children: [
CustomButton( AppCustomChipWidget(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescription.appointmentDate), false), labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescription.appointmentDate), false),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
), ),
SizedBox(width: 8.h), AppCustomChipWidget(
CustomButton( labelText: model.isSortByClinic ? prescription.name ?? "" : prescription.clinicDescription!,
text: model.isSortByClinic ? prescription.name! : prescription.clinicDescription!,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
), ),
], ],
), ),

@ -0,0 +1,199 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/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/services_price_list/services_price_list_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:provider/provider.dart';
import 'package:shimmer/shimmer.dart';
class ServicesPriceListPage extends StatefulWidget {
const ServicesPriceListPage({super.key});
@override
State<ServicesPriceListPage> createState() => _ServicesPriceListPageState();
}
class _ServicesPriceListPageState extends State<ServicesPriceListPage> {
late ServicesPriceListViewModel servicesPriceListViewModel;
late AppState appState;
@override
void initState() {
scheduleMicrotask(() {
servicesPriceListViewModel.initServicesPriceListProvider();
});
super.initState();
}
@override
Widget build(BuildContext context) {
servicesPriceListViewModel = Provider.of<ServicesPriceListViewModel>(context, listen: false);
appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.servicePriceList.tr(context: context),
child: SingleChildScrollView(
padding: EdgeInsets.all(24.h),
child: Consumer<ServicesPriceListViewModel>(builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
child: LocaleKeys.servicePriceListDesc.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
),
),
SizedBox(height: 16.h),
model.isServicesPriceListLoading
? ListView.separated(
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: NeverScrollableScrollPhysics(),
itemCount: 5,
separatorBuilder: (context, index) => SizedBox(height: 12.h),
itemBuilder: (context, index) => _buildLoadingCard(),
)
: model.filteredServicesPriceList.isEmpty
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
padding: EdgeInsets.all(40.h),
child: Center(
child: LocaleKeys.noDataAvailable.tr(context: context).toText16(
color: AppColors.textColorLight,
),
),
)
: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.serviceName.tr(context: context).toText18(weight: FontWeight.bold, color: AppColors.textColor),
SizedBox(height: 16.h),
ListView.separated(
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: NeverScrollableScrollPhysics(),
itemCount: model.filteredServicesPriceList.length,
separatorBuilder: (context, index) => Divider(height: 1.h, color: AppColors.dividerColor).withVerticalPadding(),
itemBuilder: (context, index) {
final service = model.filteredServicesPriceList[index];
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: (appState.isArabic() ? service.nameAR ?? service.nameEN ?? '' : service.nameEN ?? service.nameAR ?? '').toText16(
weight: FontWeight.w500,
color: AppColors.textColor,
),
),
SizedBox(width: 12.w),
Utils.getPaymentAmountWithSymbol('${service.price ?? 0}'.toText16(isBold: true), AppColors.blackColor, 13, isSaudiCurrency: true),
],
),
],
),
),
),
);
},
),
],
),
),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
child: LocaleKeys.servicePriceListRights.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
),
),
],
);
}),
),
),
);
}
Widget _buildLoadingCard() {
return Shimmer.fromColors(
baseColor: AppColors.greyColor.withValues(alpha: 0.3),
highlightColor: AppColors.greyColor.withValues(alpha: 0.1),
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Container(
height: 20.h,
decoration: BoxDecoration(
color: AppColors.greyColor,
borderRadius: BorderRadius.circular(4.r),
),
),
),
SizedBox(width: 12.w),
Container(
width: 80.w,
height: 32.h,
decoration: BoxDecoration(
color: AppColors.greyColor,
borderRadius: BorderRadius.circular(12.r),
),
),
],
),
),
),
);
}
}

@ -24,8 +24,8 @@ class HealthDashboard extends StatefulWidget {
class _HealthDashboardState extends State<HealthDashboard> with SingleTickerProviderStateMixin { class _HealthDashboardState extends State<HealthDashboard> with SingleTickerProviderStateMixin {
late TabController _tabController; late TabController _tabController;
final dateFormat = DateFormat('MMM dd, yyyy'); final dateFormat = DateFormat('MMM dd, yyyy', "en-US");
final timeFormat = DateFormat('hh:mm a'); final timeFormat = DateFormat('hh:mm a', "en-US");
@override @override
void initState() { void initState() {

@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/route_extensions.dart'; import 'package:hmg_patient_app_new/extensions/route_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -39,13 +40,18 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
if (appState.isAuthenticated) { if (appState.isAuthenticated) {
final user = appState.getAuthenticatedUser(); final user = appState.getAuthenticatedUser();
getIt.get<HmgServicesViewModel>().getPatientVitalSign();
if (user == null) return; if (user == null) return;
// Populate gender (gender is int: 1=Male, 2=Female) // Populate gender (gender is int: 1=Male, 2=Female)
// Use internal keys (male/female) for storage // Use internal keys (male/female) for storage
if (user.gender != null) { if (user.gender != null) {
String genderKey = user.gender == 1 ? "male" : user.gender == 2 ? "female" : "other"; String genderKey = user.gender == 1
? "male"
: user.gender == 2
? "female"
: "other";
viewModel.setGender(genderKey); viewModel.setGender(genderKey);
} }
@ -64,13 +70,14 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
// If not authenticated or fields are empty, user will fill them manually // If not authenticated or fields are empty, user will fill them manually
} }
_buildEditInfoTile({ Widget _buildEditInfoTile({
required String leadingIcon, required String leadingIcon,
required String title, required String title,
required String subTitle, required String subTitle,
required VoidCallback onTap, required VoidCallback onTap,
required String trailingIcon, required String trailingIcon,
required BuildContext context, required BuildContext context,
required HmgServicesViewModel hmgServicesVM,
Color? iconColor, Color? iconColor,
}) { }) {
return InkWell( return InkWell(
@ -92,7 +99,9 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
title.toText14(weight: FontWeight.w500), title.toText14(weight: FontWeight.w500),
subTitle.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), subTitle
.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500)
.toShimmer2(isShow: (leadingIcon == AppAssets.rulerIcon || leadingIcon == AppAssets.weightScale) && hmgServicesVM.isVitalSignLoading),
], ],
), ),
], ],
@ -144,6 +153,7 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
age--; age--;
} }
userAgeFromDOB = age; userAgeFromDOB = age;
getIt.get<SymptomsCheckerViewModel>().setSelectedAge(userAgeFromDOB);
} catch (e) { } catch (e) {
// If date parsing fails, ignore // If date parsing fails, ignore
} }
@ -154,25 +164,20 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: Consumer<SymptomsCheckerViewModel>( body: Consumer2<SymptomsCheckerViewModel, HmgServicesViewModel>(
builder: (context, viewModel, child) { builder: (context, viewModel, hmgServicesVM, child) {
// Check if any field is empty // Check if any field is empty
bool hasEmptyFields = viewModel.selectedGender == null || bool hasEmptyFields = viewModel.selectedGender == null || viewModel.selectedAge == null || viewModel.selectedHeight == null || viewModel.selectedWeight == null;
viewModel.selectedAge == null ||
viewModel.selectedHeight == null ||
viewModel.selectedWeight == null;
// Get display values // Get display values
String genderText = _getLocalizedGender(viewModel.selectedGender, context); String genderText = _getLocalizedGender(viewModel.selectedGender, context);
// Show age calculated from DOB (prefer viewModel's age, fallback to calculated from user's DOB) // Show age calculated from DOB (prefer viewModel's age, fallback to calculated from user's DOB)
int? displayAge = viewModel.selectedAge ?? userAgeFromDOB; int? displayAge = viewModel.selectedAge ?? userAgeFromDOB;
String ageText = displayAge != null ? "$displayAge ${LocaleKeys.years.tr(context: context)}" : LocaleKeys.notSet.tr(context: context); String ageText = displayAge != null ? "$displayAge ${LocaleKeys.years.tr(context: context)}" : LocaleKeys.notSet.tr(context: context);
String heightText = viewModel.selectedHeight != null
? "${viewModel.selectedHeight!.round()} ${viewModel.isHeightCm ? 'cm' : 'ft'}" String heightText = viewModel.selectedHeight != null ? "${viewModel.selectedHeight!.round()} ${viewModel.isHeightCm ? 'cm' : 'ft'}" : LocaleKeys.notSet.tr(context: context);
: LocaleKeys.notSet.tr(context: context);
String weightText = viewModel.selectedWeight != null String weightText = viewModel.selectedWeight != null ? "${viewModel.selectedWeight!.round()} ${viewModel.isWeightKg ? 'kg' : 'lbs'}" : LocaleKeys.notSet.tr(context: context);
? "${viewModel.selectedWeight!.round()} ${viewModel.isWeightKg ? 'kg' : 'lbs'}"
: LocaleKeys.notSet.tr(context: context);
return Column( return Column(
children: [ children: [
@ -195,53 +200,53 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
), ),
SizedBox(height: 32.h), SizedBox(height: 32.h),
_buildEditInfoTile( _buildEditInfoTile(
context: context, context: context,
leadingIcon: AppAssets.genderIcon, leadingIcon: AppAssets.genderIcon,
title: LocaleKeys.gender.tr(context: context), title: LocaleKeys.gender.tr(context: context),
subTitle: genderText, subTitle: genderText,
onTap: () { onTap: () {
viewModel.setUserInfoPage(0, isSinglePageEdit: true); viewModel.setUserInfoPage(0, isSinglePageEdit: true);
context.navigateWithName(AppRoutes.userInfoFlowManager); context.navigateWithName(AppRoutes.userInfoFlowManager);
}, },
trailingIcon: AppAssets.edit_icon, trailingIcon: AppAssets.edit_icon,
), hmgServicesVM: hmgServicesVM),
_getDivider(), _getDivider(),
_buildEditInfoTile( _buildEditInfoTile(
context: context, context: context,
leadingIcon: AppAssets.calendarGrey, leadingIcon: AppAssets.calendarGrey,
title: LocaleKeys.age.tr(context: context), title: LocaleKeys.age.tr(context: context),
subTitle: ageText, subTitle: ageText,
iconColor: AppColors.greyTextColor, iconColor: AppColors.greyTextColor,
onTap: () { onTap: () {
viewModel.setUserInfoPage(1, isSinglePageEdit: true); viewModel.setUserInfoPage(1, isSinglePageEdit: true);
context.navigateWithName(AppRoutes.userInfoFlowManager); context.navigateWithName(AppRoutes.userInfoFlowManager);
}, },
trailingIcon: AppAssets.edit_icon, trailingIcon: AppAssets.edit_icon,
), hmgServicesVM: hmgServicesVM),
_getDivider(), _getDivider(),
_buildEditInfoTile( _buildEditInfoTile(
context: context, context: context,
leadingIcon: AppAssets.rulerIcon, leadingIcon: AppAssets.rulerIcon,
title: LocaleKeys.height.tr(context: context), title: LocaleKeys.height.tr(context: context),
subTitle: heightText, subTitle: heightText,
onTap: () { onTap: () {
viewModel.setUserInfoPage(2, isSinglePageEdit: true); viewModel.setUserInfoPage(2, isSinglePageEdit: true);
context.navigateWithName(AppRoutes.userInfoFlowManager); context.navigateWithName(AppRoutes.userInfoFlowManager);
}, },
trailingIcon: AppAssets.edit_icon, trailingIcon: AppAssets.edit_icon,
), hmgServicesVM: hmgServicesVM),
_getDivider(), _getDivider(),
_buildEditInfoTile( _buildEditInfoTile(
context: context, context: context,
leadingIcon: AppAssets.weightScale, leadingIcon: AppAssets.weightScale,
title: LocaleKeys.weight.tr(context: context), title: LocaleKeys.weight.tr(context: context),
subTitle: weightText, subTitle: weightText,
onTap: () { onTap: () {
viewModel.setUserInfoPage(3, isSinglePageEdit: true); viewModel.setUserInfoPage(3, isSinglePageEdit: true);
context.navigateWithName(AppRoutes.userInfoFlowManager); context.navigateWithName(AppRoutes.userInfoFlowManager);
}, },
trailingIcon: AppAssets.edit_icon, trailingIcon: AppAssets.edit_icon,
), hmgServicesVM: hmgServicesVM),
], ],
), ),
), ),
@ -291,6 +296,7 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
text: LocaleKeys.yesItIs.tr(context: context), text: LocaleKeys.yesItIs.tr(context: context),
icon: AppAssets.tickIcon, icon: AppAssets.tickIcon,
iconColor: hasEmptyFields ? AppColors.greyTextColor : AppColors.whiteColor, iconColor: hasEmptyFields ? AppColors.greyTextColor : AppColors.whiteColor,
isDisabled: getIt.get<HmgServicesViewModel>().isVitalSignLoading || hasEmptyFields,
onPressed: hasEmptyFields onPressed: hasEmptyFields
? () {} // Empty function for disabled state ? () {} // Empty function for disabled state
: () => context.navigateWithName(AppRoutes.organSelectorPage), : () => context.navigateWithName(AppRoutes.organSelectorPage),

@ -158,7 +158,7 @@ class HeightSelectionPage extends StatelessWidget {
enableSound: true, enableSound: true,
minValue: minValue, minValue: minValue,
maxValue: maxValue, maxValue: maxValue,
initialHeight: viewModel.selectedHeight ?? 100, initialHeight: (viewModel.selectedHeight ?? 100).toDouble(),
isCm: viewModel.isHeightCm, isCm: viewModel.isHeightCm,
onHeightChanged: (newHeight) { onHeightChanged: (newHeight) {
log("height: $newHeight"); log("height: $newHeight");

@ -150,7 +150,7 @@ class WeightSelectionPage extends StatelessWidget {
enableSound: true, enableSound: true,
minValue: minValue, minValue: minValue,
maxValue: maxValue, maxValue: maxValue,
initialWeight: viewModel.selectedWeight!, initialWeight: (viewModel.selectedWeight ?? 70).toDouble(),
isKg: isKg, isKg: isKg,
onWeightChanged: (newWeight) { onWeightChanged: (newWeight) {
log("weight: $newWeight"); log("weight: $newWeight");

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save