Refund Request Started

faiz_dev
faizatflutter 2 days ago
parent 285287e596
commit 7cf593443d

@ -1780,11 +1780,11 @@
"illurgyInfomation": "الحساسية",
"vaccineInfomation": "التطعيمات",
"approvals1": "موافقات",
"convertBloodcholesterolInfo":"حول قيمة كوليسترول الدم بين ملجم/ديسيلتر و مليمول/لتر",
"convertBloodcholesterolInfo": "حول قيمة كوليسترول الدم بين ملجم/ديسيلتر و مليمول/لتر",
"carbsAlert": "حساب نسب المغذيات (الكربوهيدرات ،البروتين والدهون) بالسعرات الحرارية والقرامات وفقا لنسب محددة مسبقا",
"bodyFatAlert": "يحسب بناءً على الحجم",
"ibwAlert": "حساب الوزن المثالي بناءً على الطول، الوزن، وبنية الجسم",
"glucoseAlert":"تغيير وحدات القياس بين مليجرام لكل ديسيلتر و ملي مول لكل لتر",
"glucoseAlert": "تغيير وحدات القياس بين مليجرام لكل ديسيلتر و ملي مول لكل لتر",
"selectBloodGroup": "اختر الفصيلة",
"routePrescription": "طريقة الاستخدام",
"noNearestER": "لا يوجد طوارئ قريب منك",
@ -1820,9 +1820,9 @@
"dragToMoveAround": "اسحب للتحرك",
"startExploring": "ابدأ الاستكشاف",
"symptomsCheckSuggestions": "الاحتمالات",
"appointmentArrived": "تم الحضور",
"appointmentArrived": "تم الحضور",
"calendarPermissionAlertForAppointment": "يرجى منح إذن الوصول إلى التقويم من إعدادات التطبيق لضبط تذكير الموعد",
"rechargePageKey": "شحن المحفظة",
"rechargePageKey": "شحن المحفظة",
"selectFamilyFile": "اختيار ",
"ancillaryOrdersListNew": "طلبات غير مكتملة",
"medicalReportNew": "التقارير الطبية",
@ -1845,5 +1845,6 @@
"maxOneFileAllowed": "يمكن رفع ملف واحد فقط",
"fileSizeExceedsLimit": "يجب ألا يتجاوز حجم الملف 1 ميغابايت",
"completedPrescriptionOrder": "تمت الخدمة",
"submitEReferral": "تقديم الطلب"
"submitEReferral": "تقديم الطلب",
"requestRefund": "طلب استرداد"
}

@ -883,7 +883,7 @@
"selectDate": "Select Date",
"historyFlowchart": "History FlowChart",
"to": "to",
"startDate" : "Start Date",
"startDate": "Start Date",
"endDate": "End Date",
"hmgHospitals": "HMG Hospitals",
"hmcMedicalClinic": "HMC Medical Centers",
@ -896,7 +896,7 @@
"walkin": "Booked",
"continueString": "Continue",
"laserClinic": "Laser Clinic",
"covid_info" :"Dr. Sulaiman Al Habib hospitals are conducting a test for the emerging corona virus and issuing travel certificates 24/7 in a short time and with high accuracy. Those wishing to benefit from this service can visit one of Dr. Sulaiman Al Habib branches to conduct a corona test within few minutes, and obtain the result within several hours. Corona Virus Covid 19 testing service with PCR technology to detect the virus according to the highest international standards and with the latest high-precision RT-PCR devices (American GeneXpert and others), That is approved by the Food and Drug Authority as well as by the Saudi Center for Infectious Diseases Prevention.",
"covid_info": "Dr. Sulaiman Al Habib hospitals are conducting a test for the emerging corona virus and issuing travel certificates 24/7 in a short time and with high accuracy. Those wishing to benefit from this service can visit one of Dr. Sulaiman Al Habib branches to conduct a corona test within few minutes, and obtain the result within several hours. Corona Virus Covid 19 testing service with PCR technology to detect the virus according to the highest international standards and with the latest high-precision RT-PCR devices (American GeneXpert and others), That is approved by the Food and Drug Authority as well as by the Saudi Center for Infectious Diseases Prevention.",
"appointmentDetails": "Appointment Details",
"checkingDoctorAvailability": "Checking doctor availability...",
"cancellingAppointmentPleaseWait": "Cancelling Appointment, Please Wait...",
@ -1747,7 +1747,7 @@
"onlineCheckIn24HoursOnly": "Online Check-In is only available within 4 hours of the appointment time.",
"searchFor": "Search For",
"m": "m",
"inch": "in",
"inch": "in",
"updateEmailAddressTitle": "Enter the new email address to be updated: ",
"enterNewNewContactNumber": "Enter the new emergency contact number to be updated: ",
"nationalIdFileNumber": "National ID/File Number",
@ -1765,10 +1765,10 @@
"selectCriteria": "Select Criteria",
"locationServicesDisabled": "Weather is unavailable because location services are disabled. Please enable location services to see the weather information.",
"selectDateTimeKey": "Select",
"carbsAlert":"Calculate carbohydrate based protein and fat ration in calories and grams according to a pre set ratio.",
"carbsAlert": "Calculate carbohydrate based protein and fat ration in calories and grams according to a pre set ratio.",
"bodyFatAlert": "Estimate the total body fat based on the size.",
"ibwAlert": "Calculates the ideal body weight based on height, weight and body size.",
"glucoseAlert":"Convert blood glucose values between mg/dL and mmol/L. (1 mmol/L ≈ 18.0182 mg/dL)",
"glucoseAlert": "Convert blood glucose values between mg/dL and mmol/L. (1 mmol/L ≈ 18.0182 mg/dL)",
"selectBloodGroup": "Select Blood Group",
"routePrescription": "Route",
"noNearestER": "No nearest Er Around you",
@ -1810,9 +1810,9 @@
"dragToMoveAround": "Drag to move around",
"startExploring": "Start exploring",
"symptomsCheckSuggestions": "Suggestions",
"appointmentArrived": "Arrived",
"appointmentArrived": "Arrived",
"calendarPermissionAlertForAppointment": "Please grant calendar access permission from app settings to set appointment reminder.",
"rechargePageKey": "Recharge Wallet",
"rechargePageKey": "Recharge Wallet",
"selectFamilyFile": "Select",
"ancillaryOrdersListNew": "Ancillary Orders List",
"medicalReportNew": "Medical Report",
@ -1835,7 +1835,8 @@
"maxOneFileAllowed": "Only 1 file can be uploaded",
"fileSizeExceedsLimit": "File size must not exceed 1 MB",
"completedPrescriptionOrder": "Completed",
"submitEReferral": "Submit"
"submitEReferral": "Submit",
"requestRefund": "Request Refund"
}

@ -601,7 +601,7 @@ SPEC CHECKSUMS:
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
PayFortSDK: 233eabe9a45601fdbeac67fa6e5aae46ed8faf82
PayFortSDK: e12f79051ae15b2e29536771a45354900180bfcb
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
Polyline: 2a1f29f87f8d9b7de868940f4f76deb8c678a5b1
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
@ -612,7 +612,7 @@ SPEC CHECKSUMS:
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
Solar-dev: 4612dc9878b9fed2667d23b327f1d4e54e16e8d0
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
SwiftProtobuf: e1b437c8e31a4c5577b643249a0bb62ed4f02153
SwiftProtobuf: 9e106a71456f4d3f6a3b0c8fd87ef0be085efc38
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa

@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
1587365701FF0D2AAAFED0D8 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A83C76CFF9D3F92EDE3B22B4 /* Pods_Runner.framework */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
478CFA942E638C8E0064F3D7 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */; };
@ -25,7 +26,6 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
DE1EF78253E64BE86845D2CC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D562310E31D1DDEFA02A6C12 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -52,11 +52,13 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
05E1B2A0E6844572ADA81174 /* 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>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3EAE1F31D8D0AA94CA6C11EB /* 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>"; };
478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
478CFA952E6E20A60064F3D7 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
61243B3D2EC5FA3700D46FA0 /* HMGPenguinInPlatformBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HMGPenguinInPlatformBridge.swift; sourceTree = "<group>"; };
@ -70,11 +72,10 @@
687F8A672F828A6300B0FFC1 /* PenNavUI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = PenNavUI.xcframework; 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>"; };
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>"; };
769C9BF82E6F106D009F68A9 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = "<group>"; };
76AA18AC2F3B2A4D00DC8DFC /* ring_30Sec.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = ring_30Sec.caf; 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>"; };
7BEF44ABFEF585D64D3F266C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -82,8 +83,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D562310E31D1DDEFA02A6C12 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D6BB17A036DF7FCE75271203 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
A83C76CFF9D3F92EDE3B22B4 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -91,10 +91,10 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DE1EF78253E64BE86845D2CC /* Pods_Runner.framework in Frameworks */,
687F8A682F828A6300B0FFC1 /* PenguinINRenderer.xcframework in Frameworks */,
687F8A692F828A6300B0FFC1 /* Penguin.xcframework in Frameworks */,
687F8A6A2F828A6300B0FFC1 /* PenNavUI.xcframework in Frameworks */,
1587365701FF0D2AAAFED0D8 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -135,7 +135,7 @@
687F8A652F828A6300B0FFC1 /* Penguin.xcframework */,
687F8A662F828A6300B0FFC1 /* PenguinINRenderer.xcframework */,
687F8A672F828A6300B0FFC1 /* PenNavUI.xcframework */,
D562310E31D1DDEFA02A6C12 /* Pods_Runner.framework */,
A83C76CFF9D3F92EDE3B22B4 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -143,9 +143,9 @@
79DD2093A1D9674C94359FC8 /* Pods */ = {
isa = PBXGroup;
children = (
8E12CEEB8E334EE22D5259D7 /* Pods-Runner.debug.xcconfig */,
7595037DD52211B91157B0F3 /* Pods-Runner.release.xcconfig */,
D6BB17A036DF7FCE75271203 /* Pods-Runner.profile.xcconfig */,
3EAE1F31D8D0AA94CA6C11EB /* Pods-Runner.debug.xcconfig */,
05E1B2A0E6844572ADA81174 /* Pods-Runner.release.xcconfig */,
7BEF44ABFEF585D64D3F266C /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@ -227,15 +227,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
BFED6CCFE59BB148875A533B /* [CP] Check Pods Manifest.lock */,
EA10FC42B68E41E75A052FAA /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
8372B02399CDF54531650AD4 /* [CP] Embed Pods Frameworks */,
81DE7C26F41956799E954FCE /* [CP] Copy Pods Resources */,
765A5A922F35CD8B0003FF7D /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
5D78F615DF2D966539B34DE2 /* [CP] Embed Pods Frameworks */,
4AA9AE39AD56A64A1F16E548 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -325,7 +325,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
81DE7C26F41956799E954FCE /* [CP] Copy Pods Resources */ = {
4AA9AE39AD56A64A1F16E548 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -342,7 +342,7 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
8372B02399CDF54531650AD4 /* [CP] Embed Pods Frameworks */ = {
5D78F615DF2D966539B34DE2 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -374,7 +374,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
BFED6CCFE59BB148875A533B /* [CP] Check Pods Manifest.lock */ = {
EA10FC42B68E41E75A052FAA /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (

@ -4,7 +4,7 @@ import 'package:hmg_patient_app_new/core/enums.dart';
class ApiConsts {
static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@ -269,6 +269,9 @@ class ApiConsts {
// static String getAiOverViewLabOrders = "Services/Patients.svc/REST/HMGAI_Lab_Analyze_Orders_API";
// static String getAiOverViewLabOrder = "Services/Patients.svc/REST/HMGAI_Lab_Analyzer_API";
//Refund Request
static String getRefundableInvoices = "Services/Outps.svc/REST/getRefundableInvoices";
static String getAiAnalysisURL = "https://api.labpredict.com";
static String getAiRadiologyAnalysis = "$getAiAnalysisURL/radiology";
static String getAiOverViewLabOrdersNew = "$getAiAnalysisURL/analyze_lab_orders";

@ -4,6 +4,7 @@ import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/features/active_prescriptions/active_prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/ask_doctor/ask_doctor_repo.dart';
import 'package:hmg_patient_app_new/features/ask_doctor/ask_doctor_view_model.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart';
@ -12,8 +13,6 @@ import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_repo.
import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.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_view_model.dart';
import 'package:hmg_patient_app_new/features/active_prescriptions/active_prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/active_prescriptions/models/active_prescriptions_response_model.dart';
import 'package:hmg_patient_app_new/features/common/common_repo.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_repo.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
@ -23,6 +22,7 @@ import 'package:hmg_patient_app_new/features/emergency_services/emergency_servic
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_repo.dart';
import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_view_model.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_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart';
@ -55,6 +55,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/radiology/radiology_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/features/refund_request/refund_request_repo.dart';
import 'package:hmg_patient_app_new/features/refund_request/refund_request_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';
@ -66,7 +68,6 @@ import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_repo.da
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart';
import 'package:hmg_patient_app_new/features/weather/weather_repo.dart';
import 'package:hmg_patient_app_new/features/weather/weather_view_model.dart';
import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_view_model.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/app_lifecycle_service.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
@ -83,13 +84,11 @@ import 'package:local_auth/local_auth.dart';
import 'package:logger/web.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../features/monthly_reports/monthly_reports_repo.dart';
import '../features/qr_parking/qr_parking_view_model.dart';
import '../presentation/health_calculators_and_converts/health_calculator_view_model.dart';
import '../features/active_prescriptions/active_prescriptions_repo.dart';
import '../features/monthly_reports/terms_conditions_repo.dart';
import '../features/monthly_reports/terms_conditions_view_model.dart';
import '../features/qr_parking/qr_parking_view_model.dart';
import '../presentation/health_calculators_and_converts/health_calculator_view_model.dart';
GetIt getIt = GetIt.instance;
@ -176,18 +175,14 @@ class AppDependencies {
getIt.registerLazySingleton<MonthlyReportRepo>(() => MonthlyReportRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<ActivePrescriptionsRepo>(() => ActivePrescriptionsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<TermsConditionsRepo>(() => TermsConditionsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerFactory<TermsConditionsViewModel>(
() => TermsConditionsViewModel(
termsConditionsRepo: getIt<TermsConditionsRepo>(),
errorHandlerService: getIt<ErrorHandlerService>(),
),
);
getIt.registerFactory<TermsConditionsViewModel>(() => TermsConditionsViewModel(termsConditionsRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<MonthlyReportsRepo>(() => MonthlyReportsRepoImp(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<AskDoctorRepo>(() => AskDoctorRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<ServicesPriceListRepo>(() => ServicesPriceListRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<ProfileSettingsRepo>(() => ProfileSettingsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<RefundRequestRepo>(() => RefundRequestRepoImp(apiClient: getIt(), loggerService: getIt()));
// ViewModels
// Global/shared VMs LazySingleton
@ -198,13 +193,16 @@ class AppDependencies {
() => RadiologyViewModel(radiologyRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()),
);
getIt.registerLazySingleton<PrescriptionsViewModel>(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton<PrescriptionsViewModel>(
() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton<InsuranceViewModel>(() => InsuranceViewModel(insuranceRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<MyAppointmentsViewModel>(() => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
getIt.registerLazySingleton<MyAppointmentsViewModel>(
() => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
getIt.registerLazySingleton<AppointmentRatingViewModel>(() => AppointmentRatingViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
getIt.registerLazySingleton<AppointmentRatingViewModel>(
() => AppointmentRatingViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
getIt.registerLazySingleton<PayfortViewModel>(
() => PayfortViewModel(payfortRepo: getIt(), errorHandlerService: getIt()),
@ -223,7 +221,12 @@ class AppDependencies {
getIt.registerLazySingleton<BookAppointmentsViewModel>(
() => BookAppointmentsViewModel(
bookAppointmentsRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), myAppointmentsViewModel: getIt(), locationUtils: getIt(), dialogService: getIt()),
bookAppointmentsRepo: getIt(),
errorHandlerService: getIt(),
navigationService: getIt(),
myAppointmentsViewModel: getIt(),
locationUtils: getIt(),
dialogService: getIt()),
);
getIt.registerLazySingleton<ImmediateLiveCareViewModel>(
@ -237,7 +240,13 @@ class AppDependencies {
getIt.registerLazySingleton<AuthenticationViewModel>(
() => AuthenticationViewModel(
authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()),
authenticationRepo: getIt(),
cacheService: getIt(),
navigationService: getIt(),
dialogService: getIt(),
appState: getIt(),
errorHandlerService: getIt(),
localAuthService: getIt()),
);
getIt.registerLazySingleton<ProfileSettingsViewModel>(() => ProfileSettingsViewModel(
@ -310,22 +319,33 @@ class AppDependencies {
getIt.registerLazySingleton<WaterMonitorViewModel>(() => WaterMonitorViewModel(waterMonitorRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<WeatherMonitorViewModel>(() => WeatherMonitorViewModel(weatherRepo: getIt(), errorHandlerService: getIt(), locationUtils: getIt()));
getIt.registerLazySingleton<WeatherMonitorViewModel>(
() => WeatherMonitorViewModel(weatherRepo: getIt(), errorHandlerService: getIt(), locationUtils: getIt()));
//commenting this because its already define there was on run time error because of this.
getIt.registerLazySingleton<MyInvoicesViewModel>(() => MyInvoicesViewModel(myInvoicesRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton<MyInvoicesViewModel>(
() => MyInvoicesViewModel(myInvoicesRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton<MonthlyReportViewModel>(() => MonthlyReportViewModel(errorHandlerService: getIt(), monthlyReportRepo: getIt()));
getIt.registerLazySingleton<NotificationsViewModel>(() => NotificationsViewModel(notificationsRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<HealthTrackersViewModel>(() => HealthTrackersViewModel(healthTrackersRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<ActivePrescriptionsViewModel>(() => ActivePrescriptionsViewModel(errorHandlerService: getIt(), activePrescriptionsRepo: getIt()));
getIt.registerFactory<QrParkingViewModel>(() => QrParkingViewModel(qrParkingRepo: getIt<QrParkingRepo>(), errorHandlerService: getIt<ErrorHandlerService>(), cacheService: getIt<CacheService>()));
getIt.registerLazySingleton<ActivePrescriptionsViewModel>(
() => ActivePrescriptionsViewModel(errorHandlerService: getIt(), activePrescriptionsRepo: getIt()));
getIt.registerFactory<QrParkingViewModel>(() => QrParkingViewModel(
qrParkingRepo: getIt<QrParkingRepo>(), errorHandlerService: getIt<ErrorHandlerService>(), cacheService: getIt<CacheService>()));
getIt.registerLazySingleton<AskDoctorViewModel>(() => AskDoctorViewModel(askDoctorRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<ServicesPriceListViewModel>(() => ServicesPriceListViewModel(servicesPriceListRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<DateRangCalenderModel>(() => DateRangCalenderModel(appState: getIt(), navigationService: getIt(), dialogService: getIt()));
getIt.registerLazySingleton<ServicesPriceListViewModel>(
() => ServicesPriceListViewModel(servicesPriceListRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<DateRangCalenderModel>(
() => DateRangCalenderModel(appState: getIt(), navigationService: getIt(), dialogService: getIt()));
getIt.registerLazySingleton<RefundRequestViewModel>(() => RefundRequestViewModel(
appState: getIt(),
refundRequestRepo: getIt(),
errorHandlerService: getIt(),
));
}
}

@ -0,0 +1,90 @@
import 'dart:convert';
class RefundableInvoicesResponseModel {
final String? errorCode;
final String? message;
final List<RefundableInvoiceItem>? refundableInvoiceItems;
final int? statusCode;
RefundableInvoicesResponseModel({
this.errorCode,
this.message,
this.refundableInvoiceItems,
this.statusCode,
});
factory RefundableInvoicesResponseModel.fromJson(Map<String, dynamic> json) {
return RefundableInvoicesResponseModel(
errorCode: json['ErrorCode'] as String?,
message: json['Message'] as String?,
refundableInvoiceItems: json['RefundableInvoiceItems'] != null
? (json['RefundableInvoiceItems'] as List)
.map((item) => RefundableInvoiceItem.fromJson(item as Map<String, dynamic>))
.toList()
: null,
statusCode: json['StatusCode'] as int?,
);
}
}
class RefundableInvoiceItem {
final int? appointmentNo;
final int? clinicID;
final String? clinicName;
final int? doctorID;
final String? doctorName;
final String? invoiceDate;
final int? invoiceNo;
final String? nationalID;
final int? patientID;
final int? projectID;
RefundableInvoiceItem({
this.appointmentNo,
this.clinicID,
this.clinicName,
this.doctorID,
this.doctorName,
this.invoiceDate,
this.invoiceNo,
this.nationalID,
this.patientID,
this.projectID,
});
factory RefundableInvoiceItem.fromRawJson(String str) =>
RefundableInvoiceItem.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory RefundableInvoiceItem.fromJson(Map<String, dynamic> json) {
return RefundableInvoiceItem(
appointmentNo: json['AppointmentNo'] as int?,
clinicID: json['ClinicID'] as int?,
clinicName: json['ClinicName'] as String?,
doctorID: json['DoctorID'] as int?,
doctorName: json['DoctorName'] as String?,
invoiceDate: json['InvoiceDate'] as String?,
invoiceNo: json['InvoiceNo'] as int?,
nationalID: json['NationalID'] as String?,
patientID: json['PatientID'] as int?,
projectID: json['ProjectID'] as int?,
);
}
Map<String, dynamic> toJson() {
return {
'AppointmentNo': appointmentNo,
'ClinicID': clinicID,
'ClinicName': clinicName,
'DoctorID': doctorID,
'DoctorName': doctorName,
'InvoiceDate': invoiceDate,
'InvoiceNo': invoiceNo,
'NationalID': nationalID,
'PatientID': patientID,
'ProjectID': projectID,
};
}
}

@ -0,0 +1,81 @@
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/refund_request/models/resp_models/refundable_invoices_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class RefundRequestRepo {
Future<Either<Failure, GenericApiModel<List<RefundableInvoiceItem>>>>
getRefundableInvoices({required int projectID});
}
class RefundRequestRepoImp implements RefundRequestRepo {
final ApiClient apiClient;
final LoggerService loggerService;
RefundRequestRepoImp({required this.apiClient, required this.loggerService});
@override
Future<Either<Failure, GenericApiModel<List<RefundableInvoiceItem>>>>
getRefundableInvoices({required int projectID}) async {
// ApiClient auto-injects: PatientID, TokenID, LanguageID, PatientOutSA,
// PatientTypeID, IPAdress, generalid, VersionID, Channel
Map<String, dynamic> body = {
"ProjectID": projectID,
};
try {
GenericApiModel<List<RefundableInvoiceItem>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getRefundableInvoices,
body: body,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final refundableInvoicesObject =
response['RefundableInvoicesObject'];
if (refundableInvoicesObject != null) {
final invoiceItemsJson =
refundableInvoicesObject['RefundableInvoiceItems'];
final List<RefundableInvoiceItem> invoicesList =
invoiceItemsJson != null
? (invoiceItemsJson as List)
.map((item) => RefundableInvoiceItem.fromJson(
item as Map<String, dynamic>))
.toList()
: [];
apiResponse = GenericApiModel<List<RefundableInvoiceItem>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: invoicesList,
);
} else {
failure = DataParsingFailure("RefundableInvoicesObject is null");
}
} catch (e) {
loggerService.logError("getRefundableInvoices parsing error: $e");
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
loggerService.logError("getRefundableInvoices exception: $e");
return Left(UnknownFailure(e.toString()));
}
}
}

@ -0,0 +1,194 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/features/refund_request/models/resp_models/refundable_invoices_response_model.dart';
import 'package:hmg_patient_app_new/features/refund_request/refund_request_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
enum RefundTabType { invoices, hmgWallet }
/// Lightweight model representing a refundable procedure line item.
class RefundableProcedure {
final int procedureId;
final String procedureName;
final String? procedureNameN;
final double patientShare;
bool isSelected;
RefundableProcedure({
required this.procedureId,
required this.procedureName,
this.procedureNameN,
required this.patientShare,
this.isSelected = false,
});
}
/// Lightweight model for an invoice dropdown item.
class RefundInvoiceItem {
final int invoiceNo;
final int appointmentNo;
final int projectId;
final String displayLabel;
const RefundInvoiceItem({
required this.invoiceNo,
required this.appointmentNo,
required this.projectId,
required this.displayLabel,
});
}
class RefundRequestViewModel extends ChangeNotifier {
final AppState appState;
final RefundRequestRepo refundRequestRepo;
final ErrorHandlerService errorHandlerService;
RefundRequestViewModel({
required this.appState,
required this.refundRequestRepo,
required this.errorHandlerService,
});
// Tab
RefundTabType selectedTab = RefundTabType.invoices;
void setTab(RefundTabType tab) {
selectedTab = tab;
notifyListeners();
}
// Step (0 = select invoice+hospital, 1 = select procedures)
int currentStep = 0;
void goToStep(int step) {
currentStep = step;
notifyListeners();
}
void nextStep() {
if (currentStep < 1) {
currentStep++;
notifyListeners();
}
}
void previousStep() {
if (currentStep > 0) {
currentStep--;
notifyListeners();
}
}
// Hospital selection
HospitalsModel? selectedHospital;
void setSelectedHospital(HospitalsModel? hospital) {
selectedHospital = hospital;
// Reset invoice and procedures whenever hospital changes
selectedInvoice = null;
procedures.clear();
refundableInvoicesList.clear();
notifyListeners();
// Fetch refundable invoices for the selected hospital's project
if (hospital != null && hospital.iD != null) {
final projectID = hospital.iD is int ? hospital.iD as int : int.tryParse(hospital.iD.toString());
if (projectID != null) {
getRefundableInvoices(projectID: projectID);
}
}
}
void clearHospital() {
selectedHospital = null;
selectedInvoice = null;
procedures.clear();
notifyListeners();
}
// Invoice selection
RefundInvoiceItem? selectedInvoice;
List<RefundableInvoiceItem> refundableInvoicesList = [];
bool isRefundableInvoicesLoading = false;
Future<void> getRefundableInvoices({
required int projectID,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
isRefundableInvoicesLoading = true;
refundableInvoicesList.clear();
notifyListeners();
final result = await refundRequestRepo.getRefundableInvoices(projectID: projectID);
result.fold(
(failure) {
isRefundableInvoicesLoading = false;
notifyListeners();
if (onError != null) {
onError(failure.message);
} else {
errorHandlerService.handleError(failure: failure);
}
},
(apiResponse) {
isRefundableInvoicesLoading = false;
if (apiResponse.messageStatus == 1 && apiResponse.data != null) {
refundableInvoicesList = apiResponse.data!;
notifyListeners();
if (onSuccess != null) onSuccess(apiResponse);
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? "Failed to load invoices");
}
}
},
);
}
void setSelectedInvoice(RefundInvoiceItem invoice) {
selectedInvoice = invoice;
procedures.clear();
notifyListeners();
}
void clearInvoice() {
selectedInvoice = null;
procedures.clear();
notifyListeners();
}
// Procedures
List<RefundableProcedure> procedures = [];
bool isProceduresLoading = false;
void setProcedures(List<RefundableProcedure> list) {
procedures = list;
isProceduresLoading = false;
notifyListeners();
}
void toggleProcedureSelection(RefundableProcedure procedure) {
procedure.isSelected = !procedure.isSelected;
notifyListeners();
}
List<RefundableProcedure> get selectedProcedures => procedures.where((p) => p.isSelected).toList();
// Calculation
double get totalRefundableAmount {
double total = 0.0;
for (final p in selectedProcedures) {
total += p.patientShare;
}
return total;
}
// Validation
bool get isStep1Valid => selectedHospital != null && selectedInvoice != null;
bool get isStep2Valid => selectedProcedures.isNotEmpty;
}

@ -2,7 +2,7 @@
// ignore_for_file: constant_identifier_names
abstract class LocaleKeys {
abstract class LocaleKeys {
static const english = 'english';
static const arabic = 'arabic';
static const login = 'login';
@ -1838,5 +1838,5 @@ abstract class LocaleKeys {
static const fileSizeExceedsLimit = 'fileSizeExceedsLimit';
static const completedPrescriptionOrder = 'completedPrescriptionOrder';
static const submitEReferral = 'submitEReferral';
static const requestRefund = 'requestRefund';
}

@ -10,17 +10,16 @@ import 'package:flutter/services.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/utils.dart';
import 'package:hmg_patient_app_new/features/weather/weather_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/features/active_prescriptions/active_prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/ask_doctor/ask_doctor_view_model.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart';
import 'package:hmg_patient_app_new/features/active_prescriptions/active_prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
@ -39,22 +38,19 @@ 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/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/refund_request/refund_request_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/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/translations/custom_asset_loader.dart';
import 'package:hmg_patient_app_new/features/translations/translation_service.dart';
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart';
import 'package:hmg_patient_app_new/features/weather/weather_view_model.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_view_model.dart';
import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_view_model.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/app_lifecycle_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/app_theme.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_calendar_model.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart' show DateRangeSelectorRangeViewModel;
import 'package:provider/provider.dart';
@ -72,7 +68,6 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// flutter3_32 pub run easy_localization:generate -O ./lib/generated -f keys -o locale_keys.g.dart --source-dir ./assets/langs
class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
@ -257,11 +252,15 @@ void main() async {
),
ChangeNotifierProvider<ServicesPriceListViewModel>(
create: (_) => getIt.get<ServicesPriceListViewModel>(),
) ,
),
ChangeNotifierProvider<WeatherMonitorViewModel>(
create: (_) => getIt.get<WeatherMonitorViewModel>(),
), ChangeNotifierProvider<DateRangCalenderModel>(
),
ChangeNotifierProvider<DateRangCalenderModel>(
create: (_) => getIt.get<DateRangCalenderModel>(),
),
ChangeNotifierProvider<RefundRequestViewModel>(
create: (_) => getIt.get<RefundRequestViewModel>(),
)
], child: MyApp()),
), // Wrap your app

@ -11,8 +11,8 @@ import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_mode
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/wallet_payment_confirm_page.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/widgets/select_hospital_bottom_sheet.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/widgets/appbar/collapsing_list_view.dart';
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/input_widget.dart';
@ -44,7 +44,9 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
// Clear errors when entering recharge wallet page
WidgetsBinding.instance.addPostFrameCallback((_) {
habibWalletVM = context.read<HabibWalletViewModel>();
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!);
habibWalletVM.setSelectedRechargeType(0);
habibWalletVM.getProjectsList();
@ -97,262 +99,272 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Consumer<HabibWalletViewModel>(
builder: (context, viewModel, child) {
return Container(
height: 135.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
side: BorderSide(
color: viewModel.amountError != null ? AppColors.primaryRedBorderColor : AppColors.textColor,
width: 2.h,
),
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//TODO: Check with hussain to show AED or SAR
LocaleKeys.amount.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true),
Spacer(),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
Consumer<HabibWalletViewModel>(
builder: (context, viewModel, child) {
return Container(
height: 135.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
side: BorderSide(
color: viewModel.amountError != null ? AppColors.primaryRedBorderColor : AppColors.textColor,
width: 2.h,
),
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 150.h,
child: TextInputWidget(
controller: amountTextController,
labelText: "",
hintText: "",
isEnable: true,
prefix: null,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
autoFocus: true,
fontSize: 25.f,
padding: EdgeInsets.symmetric(horizontal: 8.h, vertical: 0.h),
focusNode: _amountFocusNode,
isWalletAmountInput: true,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
fontFamily: "Poppins",
onChange: (value) {
// Clear error when user starts typing
viewModel.clearAmountError();
},
// leadingIcon: AppAssets.student_card,
),
//TODO: Check with hussain to show AED or SAR
LocaleKeys.amount.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true),
Spacer(),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
SizedBox(
width: 150.h,
child: TextInputWidget(
controller: amountTextController,
labelText: "",
hintText: "",
isEnable: true,
prefix: null,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
autoFocus: true,
fontSize: 25.f,
padding: EdgeInsets.symmetric(horizontal: 8.h, vertical: 0.h),
focusNode: _amountFocusNode,
isWalletAmountInput: true,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
fontFamily: "Poppins",
onChange: (value) {
// Clear error when user starts typing
viewModel.clearAmountError();
},
// leadingIcon: AppAssets.student_card,
),
),
const Spacer(),
LocaleKeys.sar.tr(context: context).toText20(color: AppColors.greyTextColor, isBold: true),
],
),
const Spacer(),
LocaleKeys.sar.tr(context: context).toText20(color: AppColors.greyTextColor, isBold: true),
],
),
],
),
),
);
},
),
// Show error message if exists
Consumer<HabibWalletViewModel>(
builder: (context, viewModel, child) {
if (viewModel.amountError != null) {
return Padding(
padding: EdgeInsets.only(left: 16.w, top: 8.h),
child: Text(
viewModel.amountError!,
style: TextStyle(
color: AppColors.primaryRedColor,
fontSize: 12.f,
),
),
);
}
return SizedBox.shrink();
},
),
SizedBox(height: 24.h),
Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return Container(
decoration: BoxDecoration(
color: AppColors.whiteColor,
borderRadius: BorderRadius.circular(24.h),
border: Border.all(
color: habibWalletVM.hospitalError != null ? AppColors.primaryRedBorderColor : Colors.transparent,
width: 2.h,
),
);
},
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
// Show error message if exists
Consumer<HabibWalletViewModel>(
builder: (context, viewModel, child) {
if (viewModel.amountError != null) {
return Padding(
padding: EdgeInsets.only(left: 16.w, top: 8.h),
child: Text(
viewModel.amountError!,
style: TextStyle(
color: AppColors.primaryRedColor,
fontSize: 12.f,
),
),
);
}
return SizedBox.shrink();
},
),
SizedBox(height: 24.h),
Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return Container(
decoration: BoxDecoration(
color: AppColors.whiteColor,
borderRadius: BorderRadius.circular(24.h),
border: Border.all(
color: habibWalletVM.hospitalError != null ? AppColors.primaryRedBorderColor : Colors.transparent,
width: 2.h,
),
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.my_account_icon, width: 40.h, height: 40.h, applyThemeColor: false),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
Row(
children: [
(habibWalletVM.getSelectedRechargeTypeValue()).toText12(color: AppColors.greyTextColor, isBold: true),
"${LocaleKeys.medicalFile.tr(context: context)}: ${habibWalletVM.fileNumber}"
.toText14(color: AppColors.textColor, isBold: true, letterSpacing: -0.2, isEnglishOnly: true),
Utils.buildSvgWithAssets(
icon: AppAssets.my_account_icon, width: 40.h, height: 40.h, applyThemeColor: false),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(habibWalletVM.getSelectedRechargeTypeValue()).toText12(color: AppColors.greyTextColor, isBold: true),
"${LocaleKeys.medicalFile.tr(context: context)}: ${habibWalletVM.fileNumber}"
.toText14(color: AppColors.textColor, isBold: true, letterSpacing: -0.2, isEnglishOnly: true),
],
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
],
).onPress(() async {
habibWalletVM.setCurrentIndex(0);
showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.medicalFile.tr(context: context),
titleWidget: Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return habibWalletVM.currentIndex != 0
? IconButton(
icon: Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, width: 24.h, height: 24.h)),
padding: EdgeInsets.zero,
onPressed: () => habibWalletVM.setCurrentIndex(0),
highlightColor: Colors.transparent,
)
: LocaleKeys.medicalFile.tr(context: context).toText20(isBold: true);
}), child: Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return MultiPageBottomSheet();
}), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true);
}),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
).onPress(() async {
habibWalletVM.setCurrentIndex(0);
showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.medicalFile.tr(context: context),
titleWidget: Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return habibWalletVM.currentIndex != 0
? IconButton(
icon: Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, width: 24.h, height: 24.h)),
padding: EdgeInsets.zero,
onPressed: () => habibWalletVM.setCurrentIndex(0),
highlightColor: Colors.transparent,
)
: LocaleKeys.medicalFile.tr(context: context).toText20(isBold: true);
}), child: Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return MultiPageBottomSheet();
}), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true);
}),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.select_hospital_icon, width: 40.h, height: 40.h, applyThemeColor: false),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
Row(
children: [
LocaleKeys.hospital.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true),
SizedBox(
width: MediaQuery.of(context).size.width * 0.55,
child: (habibWalletVM.selectedHospital != null ? habibWalletVM.selectedHospital!.name : LocaleKeys.selectHospital.tr(context: context))!
.toText14(color: AppColors.textColor, isBold: true),
Utils.buildSvgWithAssets(
icon: AppAssets.select_hospital_icon, width: 40.h, height: 40.h, applyThemeColor: false),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.hospital.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true),
SizedBox(
width: MediaQuery.of(context).size.width * 0.55,
child: (habibWalletVM.selectedHospital != null
? habibWalletVM.selectedHospital!.name
: LocaleKeys.selectHospital.tr(context: context))!
.toText14(color: AppColors.textColor, isBold: true),
),
],
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
],
).onPress(() {
showCommonBottomSheetWithoutHeight(context,
title: LocaleKeys.selectHospital.tr(context: context), isDismissible: false, child: SelectHospitalBottomSheet(), callBackFunc: () {});
}),
// Show hospital error message if exists
if (habibWalletVM.hospitalError != null)
Padding(
padding: EdgeInsets.only(top: 8.h),
child: Text(
habibWalletVM.hospitalError!,
style: TextStyle(
color: AppColors.primaryRedColor,
fontSize: 12.f,
).onPress(() {
showCommonBottomSheetWithoutHeight(context,
title: LocaleKeys.selectHospital.tr(context: context),
isDismissible: false,
child: SelectHospitalBottomSheet(),
callBackFunc: () {});
}),
// Show hospital error message if exists
if (habibWalletVM.hospitalError != null)
Padding(
padding: EdgeInsets.only(top: 8.h),
child: Text(
habibWalletVM.hospitalError!,
style: TextStyle(
color: AppColors.primaryRedColor,
fontSize: 12.f,
),
),
),
),
),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.email_icon, width: 40.h, height: 40.h, applyThemeColor: false),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
children: [
LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true),
"${appState.getAuthenticatedUser()!.emailAddress}".toText14(color: AppColors.textColor, isBold: true, letterSpacing: -0.2, isEnglishOnly: true),
Utils.buildSvgWithAssets(icon: AppAssets.email_icon, width: 40.h, height: 40.h, applyThemeColor: false),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true),
"${appState.getAuthenticatedUser()!.emailAddress}"
.toText14(color: AppColors.textColor, isBold: true, letterSpacing: -0.2, isEnglishOnly: true),
],
),
],
),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
TextInputWidget(
labelText: LocaleKeys.notes.tr(context: context),
hintText: "",
controller: notesTextController,
keyboardType: TextInputType.text,
isEnable: true,
prefix: null,
autoFocus: false,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
leadingIcon: AppAssets.notes_icon,
hasError: false,
),
SizedBox(height: 8.h),
],
),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
TextInputWidget(
labelText: LocaleKeys.notes.tr(context: context),
hintText: "",
controller: notesTextController,
keyboardType: TextInputType.text,
isEnable: true,
prefix: null,
autoFocus: false,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
leadingIcon: AppAssets.notes_icon,
hasError: false,
),
SizedBox(height: 8.h),
],
),
),
);
}),
],
),
);
}),
],
),
),
),
),
),
),
),
//TODO: Handle Family member selection & Other Account Selection
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: CustomButton(
text: LocaleKeys.next.tr(context: context),
onPressed: () {
_amountFocusNode.unfocus();
FocusScope.of(context).unfocus();
//TODO: Handle Family member selection & Other Account Selection
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: CustomButton(
text: LocaleKeys.next.tr(context: context),
onPressed: () {
_amountFocusNode.unfocus();
FocusScope.of(context).unfocus();
// Use ViewModel validation method
if (habibWalletVM.validateRechargeForm(amountTextController.text)) {
habibWalletVM.setWalletRechargeAmount(num.parse(amountTextController.text.replaceAll(',', '')));
habibWalletVM.setNotesText(notesTextController.text);
// habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId.toString(), "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}",
// appState.getAuthenticatedUser()!.mobileNumber!);
Navigator.of(context).push(
CustomPageRoute(
page: WalletPaymentConfirmPage(),
),
);
}
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.appointment_pay_icon,
iconColor: Colors.white,
iconSize: 18.h,
).paddingSymmetrical(24.h, 24.h),
// Use ViewModel validation method
if (habibWalletVM.validateRechargeForm(amountTextController.text)) {
habibWalletVM.setWalletRechargeAmount(num.parse(amountTextController.text.replaceAll(',', '')));
habibWalletVM.setNotesText(notesTextController.text);
// habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId.toString(), "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}",
// appState.getAuthenticatedUser()!.mobileNumber!);
Navigator.of(context).push(
CustomPageRoute(
page: WalletPaymentConfirmPage(),
),
);
}
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.appointment_pay_icon,
iconColor: Colors.white,
iconSize: 18.h,
).paddingSymmetrical(24.h, 24.h),
),
],
),
],
),
),
),
),
);
}

File diff suppressed because one or more lines are too long

@ -40,9 +40,6 @@ import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_approvals_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_update_details_card.dart';
@ -62,6 +59,7 @@ import 'package:hmg_patient_app_new/presentation/my_invoices/my_invoices_list.da
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/presentation/radiology/radiology_orders_page.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/todo_page.dart';
import 'package:hmg_patient_app_new/presentation/refund_request/refund_request_create_page.dart';
import 'package:hmg_patient_app_new/presentation/vital_sign/vital_sign_page.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
@ -584,7 +582,6 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
)
: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.bgRedLightColor,
borderRadius: 12.r,
hasShadow: false,
),
@ -653,11 +650,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
isLargeText: true,
iconSize: 36.w,
).onPress(() {
Navigator.of(context).push(
CustomPageRoute(
page: InsuranceApprovalsPage(),
),
);
Navigator.of(context).push(CustomPageRoute(page: InsuranceApprovalsPage()));
}),
MedicalFileCard(
label: LocaleKeys.myInvoicesList.tr(context: context),
@ -666,10 +659,20 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
svgIcon: AppAssets.invoices_list_icon,
isLargeText: true,
iconSize: 36.w,
).onPress(() {
Navigator.of(context).push(CustomPageRoute(page: MyInvoicesList()));
}),
MedicalFileCard(
label: LocaleKeys.requestRefund.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.ancillary_orders_list_icon,
isLargeText: true,
iconSize: 36.w,
).onPress(() {
Navigator.of(context).push(
CustomPageRoute(
page: MyInvoicesList(),
page: const RefundRequestCreatePage(initialTabIndex: 0),
),
);
}),

@ -0,0 +1,442 @@
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_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/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/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/refund_request/refund_request_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/refund_request/widgets/select_hospital_bottom_sheet_refund.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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/stepper/stepper_widget.dart';
import 'package:provider/provider.dart';
class RefundRequestCreatePage extends StatefulWidget {
/// 0 = Invoices (default), 1 = HMG Wallet
final int initialTabIndex;
const RefundRequestCreatePage({super.key, this.initialTabIndex = 0});
@override
State<RefundRequestCreatePage> createState() => _RefundRequestCreatePageState();
}
class _RefundRequestCreatePageState extends State<RefundRequestCreatePage> {
late RefundRequestViewModel _vm;
late HabibWalletViewModel _habibWalletVM;
late AppState _appState;
// TODO: Replace with actual procedure list fetched from API per invoice
List<RefundableProcedure> _mockProceduresForInvoice(int invoiceNo) => [
RefundableProcedure(procedureId: 101, procedureName: 'Consultation', procedureNameN: 'استشارة', patientShare: 120.0),
RefundableProcedure(procedureId: 102, procedureName: 'Blood Test CBC', procedureNameN: 'تحليل دم شامل', patientShare: 85.50),
RefundableProcedure(procedureId: 103, procedureName: 'X-Ray Chest', procedureNameN: 'أشعة سينية صدر', patientShare: 200.0),
RefundableProcedure(procedureId: 104, procedureName: 'ECG', procedureNameN: 'تخطيط قلب', patientShare: 150.0),
];
@override
void initState() {
super.initState();
_appState = getIt.get<AppState>();
WidgetsBinding.instance.addPostFrameCallback((_) {
// Load hospital list the same way RechargeWalletPage does
_habibWalletVM.getProjectsList();
});
}
// Stepper
Widget _buildStepper(RefundRequestViewModel vm) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: LayoutBuilder(
builder: (context, constraints) {
// StepperWidget appends SizedBox(width: 8.h) after itself
final itemWidth = (constraints.maxWidth - 2 * 8.h) / 2;
return Row(
children: List.generate(2, (index) {
final isActive = index <= vm.currentStep;
final isThumb = index == vm.currentStep;
return StepperWidget(
itemWidth,
isActive ? AppColors.primaryRedColor : AppColors.greyColor,
isThumb,
4.h,
);
}),
);
},
),
);
}
// Step 1 exactly mirrors RechargeWalletPage hospital row
Widget _buildStep1Content(RefundRequestViewModel vm) {
final isArabic = _appState.isArabic();
return Consumer<RefundRequestViewModel>(
builder: (context, refundVM, _) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.select_hospital_icon,
width: 40.h,
height: 40.h,
applyThemeColor: false,
),
SizedBox(width: 8.h),
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
LocaleKeys.hospital.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true),
(refundVM.selectedHospital != null
? ((isArabic
? (refundVM.selectedHospital!.nameN?.toString() ?? refundVM.selectedHospital!.name)
: refundVM.selectedHospital!.name) ??
LocaleKeys.selectHospital.tr(context: context))
: LocaleKeys.selectHospital.tr(context: context))
.toText14(color: AppColors.textColor, isBold: true),
]),
],
),
Utils.buildSvgWithAssets(
icon: AppAssets.arrow_down,
width: 25.h,
height: 25.h,
),
],
).onPress(() {
showCommonBottomSheetWithoutHeight(
context,
title: LocaleKeys.selectHospital.tr(context: context),
isDismissible: false,
child: SelectHospitalBottomSheetRefund(),
callBackFunc: () {},
);
}),
),
);
},
);
}
// Step 2 procedure checkboxes
Widget _buildProcedureCard(RefundableProcedure procedure, int index) {
final isArabic = _appState.isArabic();
final name = isArabic && procedure.procedureNameN != null ? procedure.procedureNameN! : procedure.procedureName;
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 400),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
margin: EdgeInsets.only(bottom: 12.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: procedure.isSelected ? AppColors.primaryRedColor.withAlpha(12) : AppColors.whiteColor,
borderRadius: 16.h,
hasShadow: true,
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => _vm.toggleProcedureSelection(procedure),
borderRadius: BorderRadius.circular(16.h),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Checkbox(
value: procedure.isSelected,
onChanged: (_) => _vm.toggleProcedureSelection(procedure),
activeColor: AppColors.primaryRedColor,
side: BorderSide(color: AppColors.checkBoxBorderColor, width: 1.5),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4.r),
),
),
SizedBox(width: 8.w),
Expanded(child: name.toText14(isBold: true)),
SizedBox(width: 8.w),
Utils.getPaymentAmountWithSymbol(
procedure.patientShare.toStringAsFixed(2).toText14(
isBold: true,
isEnglishOnly: true,
color: AppColors.primaryRedColor,
),
AppColors.textColorLight,
13,
isSaudiCurrency: true,
),
],
),
),
),
),
),
),
),
);
}
Widget _buildStep2Content(RefundRequestViewModel vm) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Text(
'Select Procedures',
style: TextStyle(
fontSize: 20.f,
fontWeight: FontWeight.w600,
color: AppColors.blackColor,
letterSpacing: -0.5,
),
),
),
SizedBox(height: 24.h),
if (vm.isProceduresLoading)
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 24.w),
itemCount: 4,
itemBuilder: (_, index) => Container(
height: 56.h,
margin: EdgeInsets.only(bottom: 12.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.h,
),
).toShimmer2(isShow: true, radius: 16.r),
)
else if (vm.procedures.isEmpty)
Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 24.h),
child: 'No procedures available for refund'.toText14(color: AppColors.greyTextColor),
),
)
else
AnimationLimiter(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.bgScaffoldColor,
borderRadius: 24.h,
hasShadow: false,
),
padding: EdgeInsets.all(12.w),
child: Column(
children: List.generate(
vm.procedures.length,
(index) => _buildProcedureCard(vm.procedures[index], index),
),
),
),
),
),
],
);
}
// Bottom bar
Widget _buildBottomBar(RefundRequestViewModel vm) {
final isStep2 = vm.currentStep == 1;
final isWallet = vm.selectedTab == RefundTabType.hmgWallet;
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (isStep2 && !isWallet && vm.selectedProcedures.isNotEmpty) ...[
Padding(
padding: EdgeInsets.fromLTRB(24.w, 16.h, 24.w, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
'Total Refundable Amount'.toText12(
color: AppColors.textColorLight,
isBold: true,
),
Utils.getPaymentAmountWithSymbol(
vm.totalRefundableAmount.toStringAsFixed(2).toText18(
isBold: true,
isEnglishOnly: true,
color: AppColors.primaryRedColor,
),
AppColors.textColorLight,
14,
isSaudiCurrency: true,
),
],
),
),
],
CustomButton(
text: isStep2 ? LocaleKeys.submit.tr(context: context) : LocaleKeys.next.tr(context: context),
onPressed: () {
if (!isStep2) {
if (isWallet || vm.isStep1Valid) vm.nextStep();
} else {
// TODO: Submit API call
}
},
isDisabled: (!isWallet && !isStep2 && !vm.isStep1Valid) || (isStep2 && !isWallet && !vm.isStep2Valid),
).paddingSymmetrical(24.h, 24.h),
],
),
);
}
// HMG Wallet placeholder
Widget _buildHmgWalletContent() {
return KeyedSubtree(
key: const ValueKey('hmgWallet'),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Container(
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
padding: EdgeInsets.all(24.w),
child: Column(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.wallet,
iconColor: AppColors.primaryRedColor,
width: 48.h,
height: 48.h,
),
SizedBox(height: 16.h),
'HMG Wallet Refund'.toText16(isBold: true),
SizedBox(height: 8.h),
'Refund requests to HMG Wallet will be processed within 1-3 business days.'.toText14(color: AppColors.greyTextColor, isCenter: true),
],
),
),
),
);
}
// Build
@override
Widget build(BuildContext context) {
_habibWalletVM = context.read<HabibWalletViewModel>();
return Consumer<RefundRequestViewModel>(
builder: (context, vm, _) {
_vm = vm;
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
children: [
Expanded(
child: CollapsingListView(
title: LocaleKeys.requestRefund.tr(context: context),
history: () async {
// TODO: Navigate to refund history page
},
child: SingleChildScrollView(
padding: EdgeInsets.only(bottom: 32.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Tab bar
Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: CustomTabBar(
initialIndex: widget.initialTabIndex,
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.primaryRedColor.withAlpha(25),
tabs: [
CustomTabBarModel(null, 'Invoices'),
CustomTabBarModel(null, 'HMG Wallet'),
],
onTabChange: (index) {
vm.setTab(index == 0 ? RefundTabType.invoices : RefundTabType.hmgWallet);
vm.goToStep(0);
},
),
),
SizedBox(height: 24.h),
// Stepper (invoices tab only)
if (vm.selectedTab == RefundTabType.invoices) ...[
_buildStepper(vm),
SizedBox(height: 24.h),
],
// Content
AnimatedSwitcher(
duration: const Duration(milliseconds: 350),
transitionBuilder: (child, animation) => FadeTransition(
opacity: animation,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.05, 0),
end: Offset.zero,
).animate(animation),
child: child,
),
),
child: vm.selectedTab == RefundTabType.hmgWallet
? _buildHmgWalletContent()
: vm.currentStep == 0
? KeyedSubtree(
key: const ValueKey('step1'),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: _buildStep1Content(vm),
),
)
: KeyedSubtree(
key: const ValueKey('step2'),
child: _buildStep2Content(vm),
),
),
],
),
),
),
),
_buildBottomBar(vm),
],
),
);
},
);
}
}

@ -0,0 +1,223 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/doctor_response_mapper.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/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/features/refund_request/refund_request_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/widgets/hospital_list_item.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
class SelectHospitalBottomSheetRefund extends StatefulWidget {
const SelectHospitalBottomSheetRefund({super.key});
@override
State<SelectHospitalBottomSheetRefund> createState() =>
_SelectHospitalBottomSheetRefundState();
}
class _SelectHospitalBottomSheetRefundState
extends State<SelectHospitalBottomSheetRefund> {
late RefundRequestViewModel refundVM;
late HabibWalletViewModel habibWalletVM;
bool sortByLocation = false;
bool isLoading = false;
List<HospitalsModel> displayList = [];
@override
void initState() {
super.initState();
final appState = getIt.get<AppState>();
sortByLocation = (appState.userLat != 0.0) && (appState.userLong != 0.0);
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateDisplayList();
});
}
void _updateDisplayList() {
final appState = getIt.get<AppState>();
// Reuse the same hospital list already loaded in HabibWalletViewModel
final hospitals = List<HospitalsModel>.from(habibWalletVM.advancePaymentHospitals);
if (sortByLocation && appState.userLat != 0.0 && appState.userLong != 0.0) {
hospitals.sort((a, b) {
final distA = (a.latitude != null && a.longitude != null)
? DoctorMapper.calculateDistance(appState.userLat, appState.userLong,
double.parse(a.latitude!), double.parse(a.longitude!))
: double.infinity;
final distB = (b.latitude != null && b.longitude != null)
? DoctorMapper.calculateDistance(appState.userLat, appState.userLong,
double.parse(b.latitude!), double.parse(b.longitude!))
: double.infinity;
return distA.compareTo(distB);
});
}
setState(() {
displayList = hospitals;
isLoading = false;
});
}
void _refreshHospitalListAfterApi() {
void listener() {
if (habibWalletVM.advancePaymentHospitals.isNotEmpty) {
habibWalletVM.removeListener(listener);
_updateDisplayList();
}
}
habibWalletVM.addListener(listener);
habibWalletVM.getProjectsList();
}
void _handleSortByLocationToggle(bool value) {
if (value) {
final locationUtils = getIt.get<LocationUtils>();
locationUtils.getLocation(
isShowConfirmDialog: true,
onSuccess: (latLng) {
setState(() {
sortByLocation = true;
isLoading = true;
});
_refreshHospitalListAfterApi();
},
onFailure: () {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getWarningWidget(
loadingText: LocaleKeys.giveLocationPermissionForNearestList
.tr(context: context),
isShowActionButtons: true,
onCancelTap: () => Navigator.of(context).pop(),
onConfirmTap: () async {
Navigator.of(context).pop();
openAppSettings();
},
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
setState(() => sortByLocation = false);
},
onLocationDeniedForever: () {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getWarningWidget(
loadingText: LocaleKeys.giveLocationPermissionForNearestList
.tr(context: context),
isShowActionButtons: true,
onCancelTap: () => Navigator.of(context).pop(),
onConfirmTap: () async {
Navigator.of(context).pop();
openAppSettings();
},
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
setState(() => sortByLocation = false);
},
);
} else {
final appState = getIt.get<AppState>();
appState.resetLocation();
setState(() {
sortByLocation = false;
isLoading = true;
});
_refreshHospitalListAfterApi();
}
}
@override
Widget build(BuildContext context) {
refundVM = context.read<RefundRequestViewModel>();
habibWalletVM = context.read<HabibWalletViewModel>();
if (displayList.isEmpty && habibWalletVM.advancePaymentHospitals.isNotEmpty) {
displayList = List.from(habibWalletVM.advancePaymentHospitals);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.selectHospitalForAdvancePayment
.tr(context: context)
.toText16(color: AppColors.greyTextColor, isBold: true),
SizedBox(height: 16.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 4.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.location,
iconColor: AppColors.greyTextColor,
width: 18.h,
height: 18.h),
SizedBox(width: 8.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.sortByLocation.tr(context: context).toText14(isBold: true),
LocaleKeys.sortByNearestLocation
.tr(context: context)
.toText11(color: AppColors.textColorLight, isBold: true),
],
),
],
),
Switch(
value: sortByLocation,
onChanged: _handleSortByLocationToggle,
activeThumbColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: 0.15),
),
],
),
),
SizedBox(height: 8.h),
isLoading
? SizedBox(
height: MediaQuery.sizeOf(context).height * .4,
child: Center(child: Utils.getLoadingWidget()),
)
: SizedBox(
height: MediaQuery.sizeOf(context).height * .4,
child: ListView.separated(
itemCount: displayList.length,
separatorBuilder: (_, __) => SizedBox(height: 16.h),
itemBuilder: (_, index) {
return HospitalListItemAdvancePayment(
hospitalModel: displayList[index],
isLocationEnabled: sortByLocation,
).onPress(() {
refundVM.setSelectedHospital(displayList[index]);
Navigator.of(context).pop();
});
},
),
),
],
);
}
}

@ -41,7 +41,8 @@ import '../core/dependencies.dart';
import '../features/monthly_reports/monthly_reports_repo.dart';
import '../features/monthly_reports/monthly_reports_view_model.dart';
import '../features/qr_parking/qr_parking_view_model.dart';
import '../presentation/parking/paking_page.dart';
import 'package:hmg_patient_app_new/presentation/parking/paking_page.dart';
import 'package:hmg_patient_app_new/presentation/refund_request/refund_request_create_page.dart';
import '../services/error_handler_service.dart';
class AppRoutes {
@ -101,6 +102,9 @@ class AppRoutes {
// Weather
static const String familyFilesPage = '/familyFilesPage';
// Refund Request
static const String refundRequestCreatePage = '/refundRequestCreatePage';
static Map<String, WidgetBuilder> get routes => {
initialRoute: (context) => SplashPage(),
loginScreen: (context) => LoginScreen(),
@ -160,6 +164,10 @@ class AppRoutes {
qrParking: (context) => ChangeNotifierProvider<QrParkingViewModel>(
create: (_) => getIt<QrParkingViewModel>(),
child: const ParkingPage(),
)
),
refundRequestCreatePage: (context) {
final args = ModalRoute.of(context)?.settings.arguments as int?;
return RefundRequestCreatePage(initialTabIndex: args ?? 0);
},
};
}

@ -1042,13 +1042,13 @@ packages:
source: hosted
version: "1.2.1"
image_picker_android:
dependency: transitive
dependency: "direct main"
description:
name: image_picker_android
sha256: "5e9bf126c37c117cf8094215373c6d561117a3cfb50ebc5add1a61dc6e224677"
sha256: d5b3e1774af29c9ab00103afb0d4614070f924d2e0057ac867ec98800114793f
url: "https://pub.dev"
source: hosted
version: "0.8.13+10"
version: "0.8.13+17"
image_picker_for_web:
dependency: transitive
description:
@ -1082,7 +1082,7 @@ packages:
source: hosted
version: "0.2.2+1"
image_picker_platform_interface:
dependency: transitive
dependency: "direct main"
description:
name: image_picker_platform_interface
sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c"

Loading…
Cancel
Save