Merge pull request 'haroon_dev' (#332) from haroon_dev into master

Reviewed-on: https://34.17.182.140/Haroon6138/HMG_Patient_App_New/pulls/332
master
Haroon6138 2 weeks ago
commit 9a93e2ea1a

@ -86,3 +86,6 @@
@com.peng.bus.PISubscribe <methods>; @com.peng.bus.PISubscribe <methods>;
} }
-keep class com.peng.pennavmap.models.bus.BackButtonEventBusData { *; } -keep class com.peng.pennavmap.models.bus.BackButtonEventBusData { *; }
-keep interface com.payfort.fortpaymentsdk.** {*;}
-keep class com.payfort.fortpaymentsdk.** {*;}

@ -163,7 +163,7 @@
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="true"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:launchMode="singleInstance" android:launchMode="singleTop"
android:showOnLockScreen="true" android:showOnLockScreen="true"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"

@ -971,7 +971,7 @@
"immediateConsultation": "استشارة فورية", "immediateConsultation": "استشارة فورية",
"scheduledConsultation": "استشارة مجدولة", "scheduledConsultation": "استشارة مجدولة",
"pharmaLiveCare": "لايف كير الصيدلية", "pharmaLiveCare": "لايف كير الصيدلية",
"notSureHelpMeChooseClinic": "هل تريد مساعدة في اختيار العياة؟", "notSureHelpMeChooseClinic": "هل تريد مساعدة في اختيار العيادة؟",
"mentionYourSymptomsAndFindDoctors": "أدخل حالتك الصحية للحصول على قائمة الأطباء", "mentionYourSymptomsAndFindDoctors": "أدخل حالتك الصحية للحصول على قائمة الأطباء",
"immediateService": "خدمة فورية", "immediateService": "خدمة فورية",
"noNeedToWaitGetMedicalConsultation": "لا حاجة للانتظار، ستحصل على استشارة طبية فورية عبر مكالمة فيديو", "noNeedToWaitGetMedicalConsultation": "لا حاجة للانتظار، ستحصل على استشارة طبية فورية عبر مكالمة فيديو",
@ -1956,6 +1956,7 @@
"invoiceHistory": "سجل الفواتير", "invoiceHistory": "سجل الفواتير",
"refund": "استرداد", "refund": "استرداد",
"invoiceHistory": "سجل الفواتير", "invoiceHistory": "سجل الفواتير",
"thisInvoiceIsNotEligibleForRefund": "هذه الفاتورة غير مؤهلة للاسترداد", "refund": "استرداد",
"refund": "استرداد" "parkingQR": "رمز الاستجابة السريعة لوقوف السيارات",
"parkingQRDesc": "امسح رمز الاستجابة السريعة (QR Code) أدناه عند بوابة الخروج من موقف السيارات:"
} }

@ -1939,14 +1939,15 @@
"earned": "Earned", "earned": "Earned",
"getParkingQR": "Get Parking QR", "getParkingQR": "Get Parking QR",
"cannotOpenThisFile": "Cannot open this file", "cannotOpenThisFile": "Cannot open this file",
"thisInvoiceIsNotEligibleForRefund": "This invoice is not eligible for refund",
"refundDetails": "Refund Details", "refundDetails": "Refund Details",
"requestNo": "Request No.", "requestNo": "Request No.",
"referenceNo": "Reference No.", "referenceNo": "Reference No.",
"refund": "Refund", "refund": "Refund",
"advanceNumber": "Advance Number", "advanceNumber": "Advance Number",
"amountOnly": "Amount", "amountOnly": "Amount",
"invoiceHistory": "Invoice History" "invoiceHistory": "Invoice History",
"parkingQR": "Parking QR Code",
"parkingQRDesc": "Scan the below QR Code at the exit parking gate:"
} }

@ -1,626 +0,0 @@
PODS:
- amazon_payfort (1.1.4):
- Flutter
- PayFortSDK
- audio_session (0.0.1):
- Flutter
- barcode_scan2 (0.0.1):
- Flutter
- SwiftProtobuf (~> 1.33)
- connectivity_plus (0.0.1):
- Flutter
- CryptoSwift (1.8.4)
- device_calendar (0.0.1):
- Flutter
- device_calendar_plus_ios (0.0.1):
- Flutter
- device_info_plus (0.0.1):
- Flutter
- DKImagePickerController/Core (4.3.9):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
- DKImagePickerController/ImageDataManager (4.3.9)
- DKImagePickerController/PhotoGallery (4.3.9):
- DKImagePickerController/Core
- DKPhotoGallery
- DKImagePickerController/Resource (4.3.9)
- DKPhotoGallery (0.0.19):
- DKPhotoGallery/Core (= 0.0.19)
- DKPhotoGallery/Model (= 0.0.19)
- DKPhotoGallery/Preview (= 0.0.19)
- DKPhotoGallery/Resource (= 0.0.19)
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Core (0.0.19):
- DKPhotoGallery/Model
- DKPhotoGallery/Preview
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Model (0.0.19):
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Preview (0.0.19):
- DKPhotoGallery/Model
- DKPhotoGallery/Resource
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Resource (0.0.19):
- SDWebImage
- SwiftyGif
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Firebase/Analytics (11.15.0):
- Firebase/Core
- Firebase/Core (11.15.0):
- Firebase/CoreOnly
- FirebaseAnalytics (~> 11.15.0)
- Firebase/CoreOnly (11.15.0):
- FirebaseCore (~> 11.15.0)
- Firebase/Crashlytics (11.15.0):
- Firebase/CoreOnly
- FirebaseCrashlytics (~> 11.15.0)
- Firebase/Messaging (11.15.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 11.15.0)
- firebase_analytics (11.6.0):
- Firebase/Analytics (= 11.15.0)
- firebase_core
- Flutter
- firebase_core (3.15.2):
- Firebase/CoreOnly (= 11.15.0)
- Flutter
- firebase_crashlytics (4.3.10):
- Firebase/Crashlytics (= 11.15.0)
- firebase_core
- Flutter
- firebase_messaging (15.2.10):
- Firebase/Messaging (= 11.15.0)
- firebase_core
- Flutter
- FirebaseAnalytics (11.15.0):
- FirebaseAnalytics/Default (= 11.15.0)
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- FirebaseAnalytics/Default (11.15.0):
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleAppMeasurement/Default (= 11.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- FirebaseCore (11.15.0):
- FirebaseCoreInternal (~> 11.15.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1)
- FirebaseCoreExtension (11.15.0):
- FirebaseCore (~> 11.15.0)
- FirebaseCoreInternal (11.15.0):
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- FirebaseCrashlytics (11.15.0):
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- FirebaseRemoteConfigInterop (~> 11.0)
- FirebaseSessions (~> 11.0)
- GoogleDataTransport (~> 10.0)
- GoogleUtilities/Environment (~> 8.1)
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- FirebaseInstallations (11.15.0):
- FirebaseCore (~> 11.15.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- PromisesObjC (~> 2.4)
- FirebaseMessaging (11.15.0):
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleDataTransport (~> 10.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Reachability (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- nanopb (~> 3.30910.0)
- FirebaseRemoteConfigInterop (11.15.0)
- FirebaseSessions (11.15.0):
- FirebaseCore (~> 11.15.0)
- FirebaseCoreExtension (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleDataTransport (~> 10.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- nanopb (~> 3.30910.0)
- PromisesSwift (~> 2.1)
- FLAnimatedImage (1.0.17)
- Flutter (1.0.0)
- flutter_callkit_incoming (0.0.1):
- CryptoSwift
- Flutter
- flutter_image_compress_common (1.0.0):
- Flutter
- Mantle
- SDWebImage
- SDWebImageWebPCoder
- flutter_inappwebview_ios (0.0.1):
- Flutter
- flutter_inappwebview_ios/Core (= 0.0.1)
- OrderedSet (~> 6.0.3)
- flutter_inappwebview_ios/Core (0.0.1):
- Flutter
- OrderedSet (~> 6.0.3)
- flutter_ios_voip_kit_karmm (0.8.0):
- Flutter
- flutter_local_notifications (0.0.1):
- Flutter
- flutter_nfc_kit (3.6.0):
- Flutter
- flutter_zoom_videosdk (0.0.1):
- Flutter
- ZoomVideoSDK/CptShare (= 2.1.10)
- ZoomVideoSDK/zm_annoter_dynamic (= 2.1.10)
- ZoomVideoSDK/zoomcml (= 2.1.10)
- ZoomVideoSDK/ZoomVideoSDK (= 2.1.10)
- fluttertoast (0.0.2):
- Flutter
- geolocator_apple (1.2.0):
- Flutter
- FlutterMacOS
- Google-Maps-iOS-Utils (5.0.0):
- GoogleMaps (~> 8.0)
- google_maps_flutter_ios (0.0.1):
- Flutter
- Google-Maps-iOS-Utils (< 7.0, >= 5.0)
- GoogleMaps (< 11.0, >= 8.4)
- GoogleAdsOnDeviceConversion (2.1.0):
- GoogleUtilities/Logger (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/Core (11.15.0):
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/Default (11.15.0):
- GoogleAdsOnDeviceConversion (= 2.1.0)
- GoogleAppMeasurement/Core (= 11.15.0)
- GoogleAppMeasurement/IdentitySupport (= 11.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/IdentitySupport (11.15.0):
- GoogleAppMeasurement/Core (= 11.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- GoogleMaps (8.4.0):
- GoogleMaps/Maps (= 8.4.0)
- GoogleMaps/Base (8.4.0)
- GoogleMaps/Maps (8.4.0):
- GoogleMaps/Base
- GoogleUtilities/AppDelegateSwizzler (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Privacy
- GoogleUtilities/Environment (8.1.0):
- GoogleUtilities/Privacy
- GoogleUtilities/Logger (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Privacy
- GoogleUtilities/MethodSwizzler (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- GoogleUtilities/Network (8.1.0):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Privacy
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (8.1.0)":
- GoogleUtilities/Privacy
- GoogleUtilities/Privacy (8.1.0)
- GoogleUtilities/Reachability (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- GoogleUtilities/UserDefaults (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- health (13.1.4):
- Flutter
- image_picker_ios (0.0.1):
- Flutter
- in_app_review (2.0.0):
- Flutter
- just_audio (0.0.1):
- Flutter
- FlutterMacOS
- libwebp (1.5.0):
- libwebp/demux (= 1.5.0)
- libwebp/mux (= 1.5.0)
- libwebp/sharpyuv (= 1.5.0)
- libwebp/webp (= 1.5.0)
- libwebp/demux (1.5.0):
- libwebp/webp
- libwebp/mux (1.5.0):
- libwebp/demux
- libwebp/sharpyuv (1.5.0)
- libwebp/webp (1.5.0):
- libwebp/sharpyuv
- local_auth_darwin (0.0.1):
- Flutter
- FlutterMacOS
- location (0.0.1):
- Flutter
- manage_calendar_events (0.0.1):
- Flutter
- Mantle (2.2.0):
- Mantle/extobjc (= 2.2.0)
- Mantle/extobjc (2.2.0)
- map_launcher (0.0.1):
- Flutter
- MapboxCommon (23.11.0)
- MapboxCoreMaps (10.19.1):
- MapboxCommon (~> 23.11)
- MapboxCoreNavigation (2.19.0):
- MapboxDirections (~> 2.14)
- MapboxNavigationNative (< 207.0.0, >= 206.0.1)
- MapboxDirections (2.14.3):
- Polyline (~> 5.0)
- Turf (~> 2.8.0)
- MapboxMaps (10.19.0):
- MapboxCommon (= 23.11.0)
- MapboxCoreMaps (= 10.19.1)
- MapboxMobileEvents (= 2.0.0)
- Turf (= 2.8.0)
- MapboxMobileEvents (2.0.0)
- MapboxNavigation (2.19.0):
- MapboxCoreNavigation (= 2.19.0)
- MapboxMaps (~> 10.18)
- MapboxSpeech (~> 2.0)
- Solar-dev (~> 3.0)
- MapboxNavigationNative (206.2.2):
- MapboxCommon (~> 23.10)
- MapboxSpeech (2.1.1)
- nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0)
- nanopb/encode (= 3.30910.0)
- nanopb/decode (3.30910.0)
- nanopb/encode (3.30910.0)
- network_info_plus (0.0.1):
- Flutter
- open_filex (0.0.2):
- Flutter
- OrderedSet (6.0.3)
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- PayFortSDK (3.2.2)
- permission_handler_apple (9.3.0):
- Flutter
- Polyline (5.1.0)
- PromisesObjC (2.4.0)
- PromisesSwift (2.4.0):
- PromisesObjC (= 2.4.0)
- SDWebImage (5.21.5):
- SDWebImage/Core (= 5.21.5)
- SDWebImage/Core (5.21.5)
- SDWebImageWebPCoder (0.15.0):
- libwebp (~> 1.0)
- SDWebImage/Core (~> 5.17)
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- Solar-dev (3.0.1)
- sqflite_darwin (0.0.4):
- Flutter
- FlutterMacOS
- SwiftProtobuf (1.36.1)
- SwiftyGif (5.4.5)
- Turf (2.8.0)
- url_launcher_ios (0.0.1):
- Flutter
- video_player_avfoundation (0.0.1):
- Flutter
- FlutterMacOS
- wakelock_plus (0.0.1):
- Flutter
- webview_flutter_wkwebview (0.0.1):
- Flutter
- FlutterMacOS
- ZoomVideoSDK/CptShare (2.1.10)
- ZoomVideoSDK/zm_annoter_dynamic (2.1.10)
- ZoomVideoSDK/zoomcml (2.1.10)
- ZoomVideoSDK/ZoomVideoSDK (2.1.10)
DEPENDENCIES:
- amazon_payfort (from `.symlinks/plugins/amazon_payfort/ios`)
- audio_session (from `.symlinks/plugins/audio_session/ios`)
- barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_calendar (from `.symlinks/plugins/device_calendar/ios`)
- device_calendar_plus_ios (from `.symlinks/plugins/device_calendar_plus_ios/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- FLAnimatedImage
- Flutter (from `Flutter`)
- flutter_callkit_incoming (from `.symlinks/plugins/flutter_callkit_incoming/ios`)
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
- flutter_ios_voip_kit_karmm (from `.symlinks/plugins/flutter_ios_voip_kit_karmm/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_nfc_kit (from `.symlinks/plugins/flutter_nfc_kit/ios`)
- flutter_zoom_videosdk (from `.symlinks/plugins/flutter_zoom_videosdk/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
- google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`)
- health (from `.symlinks/plugins/health/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
- just_audio (from `.symlinks/plugins/just_audio/darwin`)
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
- location (from `.symlinks/plugins/location/ios`)
- manage_calendar_events (from `.symlinks/plugins/manage_calendar_events/ios`)
- map_launcher (from `.symlinks/plugins/map_launcher/ios`)
- MapboxMaps (= 10.19.0)
- MapboxNavigation (= 2.19.0)
- network_info_plus (from `.symlinks/plugins/network_info_plus/ios`)
- open_filex (from `.symlinks/plugins/open_filex/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
SPEC REPOS:
trunk:
- CryptoSwift
- DKImagePickerController
- DKPhotoGallery
- Firebase
- FirebaseAnalytics
- FirebaseCore
- FirebaseCoreExtension
- FirebaseCoreInternal
- FirebaseCrashlytics
- FirebaseInstallations
- FirebaseMessaging
- FirebaseRemoteConfigInterop
- FirebaseSessions
- FLAnimatedImage
- Google-Maps-iOS-Utils
- GoogleAdsOnDeviceConversion
- GoogleAppMeasurement
- GoogleDataTransport
- GoogleMaps
- GoogleUtilities
- libwebp
- Mantle
- MapboxCommon
- MapboxCoreMaps
- MapboxCoreNavigation
- MapboxDirections
- MapboxMaps
- MapboxMobileEvents
- MapboxNavigation
- MapboxNavigationNative
- MapboxSpeech
- nanopb
- OrderedSet
- PayFortSDK
- Polyline
- PromisesObjC
- PromisesSwift
- SDWebImage
- SDWebImageWebPCoder
- Solar-dev
- SwiftProtobuf
- SwiftyGif
- Turf
- ZoomVideoSDK
EXTERNAL SOURCES:
amazon_payfort:
:path: ".symlinks/plugins/amazon_payfort/ios"
audio_session:
:path: ".symlinks/plugins/audio_session/ios"
barcode_scan2:
:path: ".symlinks/plugins/barcode_scan2/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
device_calendar:
:path: ".symlinks/plugins/device_calendar/ios"
device_calendar_plus_ios:
:path: ".symlinks/plugins/device_calendar_plus_ios/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
firebase_analytics:
:path: ".symlinks/plugins/firebase_analytics/ios"
firebase_core:
:path: ".symlinks/plugins/firebase_core/ios"
firebase_crashlytics:
:path: ".symlinks/plugins/firebase_crashlytics/ios"
firebase_messaging:
:path: ".symlinks/plugins/firebase_messaging/ios"
Flutter:
:path: Flutter
flutter_callkit_incoming:
:path: ".symlinks/plugins/flutter_callkit_incoming/ios"
flutter_image_compress_common:
:path: ".symlinks/plugins/flutter_image_compress_common/ios"
flutter_inappwebview_ios:
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
flutter_ios_voip_kit_karmm:
:path: ".symlinks/plugins/flutter_ios_voip_kit_karmm/ios"
flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios"
flutter_nfc_kit:
:path: ".symlinks/plugins/flutter_nfc_kit/ios"
flutter_zoom_videosdk:
:path: ".symlinks/plugins/flutter_zoom_videosdk/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
geolocator_apple:
:path: ".symlinks/plugins/geolocator_apple/darwin"
google_maps_flutter_ios:
:path: ".symlinks/plugins/google_maps_flutter_ios/ios"
health:
:path: ".symlinks/plugins/health/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
in_app_review:
:path: ".symlinks/plugins/in_app_review/ios"
just_audio:
:path: ".symlinks/plugins/just_audio/darwin"
local_auth_darwin:
:path: ".symlinks/plugins/local_auth_darwin/darwin"
location:
:path: ".symlinks/plugins/location/ios"
manage_calendar_events:
:path: ".symlinks/plugins/manage_calendar_events/ios"
map_launcher:
:path: ".symlinks/plugins/map_launcher/ios"
network_info_plus:
:path: ".symlinks/plugins/network_info_plus/ios"
open_filex:
:path: ".symlinks/plugins/open_filex/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
video_player_avfoundation:
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios"
webview_flutter_wkwebview:
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
SPEC CHECKSUMS:
amazon_payfort: 782d287860ac8962baf4e0a2a0fb2f81fdf27204
audio_session: 19e9480dbdd4e5f6c4543826b2e8b0e4ab6145fe
barcode_scan2: 050b7b327e85954c8918715d42e83c5a35c3ed89
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90
device_calendar: 9cb33f88a02e19652ec7b8b122ca778f751b1f7b
device_calendar_plus_ios: 34dca387f44fad29b840096a840f94edc55af858
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
firebase_analytics: bf93e20703c95030404d6ddbb1adf05bf5c3885b
firebase_core: 99a37263b3c27536063a7b601d9e2a49400a433c
firebase_crashlytics: 986773c0c7ea4c3f5a4ead141b58522fcd9dfdb1
firebase_messaging: bf6697c61f31c7cc0f654131212ff04c0115c2c7
FirebaseAnalytics: 6433dfd311ba78084fc93bdfc145e8cb75740eae
FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e
FirebaseCoreExtension: edbd30474b5ccf04e5f001470bdf6ea616af2435
FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4
FirebaseCrashlytics: e09d0bc19aa54a51e45b8039c836ef73f32c039a
FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09
FirebaseRemoteConfigInterop: 1c6135e8a094cc6368949f5faeeca7ee8948b8aa
FirebaseSessions: b9a92c1c51bbb81e78fc3142cda6d925d700f8e7
FLAnimatedImage: bbf914596368867157cc71b38a8ec834b3eeb32b
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_callkit_incoming: 417dd1b46541cdd5d855ad795ccbe97d1c18155e
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
flutter_ios_voip_kit_karmm: 7ea37381a8841c92d186edf1f4604df5cc437579
flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f
flutter_nfc_kit: 3985c93f749b9cb4747479205c2f10bd2f877a11
flutter_zoom_videosdk: 7ddc97d617b85aeace252165d2b5fc5638678c38
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f
geolocator_apple: 66b711889fd333205763b83c9dcf0a57a28c7afd
Google-Maps-iOS-Utils: 66d6de12be1ce6d3742a54661e7a79cb317a9321
google_maps_flutter_ios: ffdd933832966673a1f57f5effd014dc8d4d43a2
GoogleAdsOnDeviceConversion: 2be6297a4f048459e0ae17fad9bfd2844e10cf64
GoogleAppMeasurement: 700dce7541804bec33db590a5c496b663fbe2539
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
health: 9cd70a5c9d9ec559a12d639a1471091c0cbb6b48
image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b
in_app_review: 436034b18594851a7328d7f1c2ed5ec235b79cfc
just_audio: a42c63806f16995daf5b219ae1d679deb76e6a79
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
local_auth_darwin: 63c73d6d28cc3e239be2b6aa460ea6e317cd5100
location: d5cf8598915965547c3f36761ae9cc4f4e87d22e
manage_calendar_events: 9b2889799340398027b3e3f5c4891d41599ec257
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
map_launcher: 8a4e484aaddff55680a7da9bc9e25f512067fb87
MapboxCommon: 119f3759f7dc9457f0695848108ab323eb643cb4
MapboxCoreMaps: ca17f67baced23f8c952166ac6314c35bad3f66c
MapboxCoreNavigation: 3be9990fae3ed732a101001746d0e3b4234ec023
MapboxDirections: d9ad8452e8927d95ed21e35f733834dbca7e0eb1
MapboxMaps: b7f29ec7c33f7dc6d2947c1148edce6db81db9a7
MapboxMobileEvents: d044b9edbe0ec7df60f6c2c9634fe9a7f449266b
MapboxNavigation: da9cf3d773ed5b0fa0fb388fccdaa117ee681f31
MapboxNavigationNative: 629e359f3d2590acd1ebbacaaf99e1a80ee57e42
MapboxSpeech: cd25ef99c3a3d2e0da72620ff558276ea5991a77
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
network_info_plus: 6613d9d7cdeb0e6f366ed4dbe4b3c51c52d567a9
open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
PayFortSDK: e12f79051ae15b2e29536771a45354900180bfcb
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
Polyline: 2a1f29f87f8d9b7de868940f4f76deb8c678a5b1
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838
SDWebImageWebPCoder: 0e06e365080397465cc73a7a9b472d8a3bd0f377
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
Solar-dev: 4612dc9878b9fed2667d23b327f1d4e54e16e8d0
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
SwiftProtobuf: 9e106a71456f4d3f6a3b0c8fd87ef0be085efc38
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa
video_player_avfoundation: 7993f492ae0bd77edaea24d9dc051d8bb2cd7c86
wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e
webview_flutter_wkwebview: 29eb20d43355b48fe7d07113835b9128f84e3af4
ZoomVideoSDK: 94e939820e57a075c5e712559f927017da0de06a
PODFILE CHECKSUM: 8235407385ddd5904afc2563d65406117a51993e
COCOAPODS: 1.16.2

@ -3,6 +3,7 @@ import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:clarity_flutter/clarity_flutter.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -209,14 +210,14 @@ class ApiClientImp implements ApiClient {
body['TokenID'] = "@dm!n"; body['TokenID'] = "@dm!n";
} }
// body['TokenID'] = "@dm!n"; body['TokenID'] = "@dm!n";
// body['PatientID'] = 4774167; // body['PatientID'] = 4774598;
// body['PatientID'] = 89754444; // body['PatientID'] = 89754444;
// body['PatientTypeID'] = 1; // body['PatientTypeID'] = 1;
// body['PatientOutSA'] = 0; // body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q"; // body['SessionID'] = "45786230487560q";
//VIP Patient: 1181868body: //VIP Patient: 1181868
// body['IdentificationNo'] = "2235558844"; // body['IdentificationNo'] = "2235558844";
// body['MobileNo'] = "966533147722"; // body['MobileNo'] = "966533147722";
@ -259,11 +260,16 @@ class ApiClientImp implements ApiClient {
body: requestBody, body: requestBody,
headers: headers, headers: headers,
); );
log("response: ${response.body.toString()}"); // log("response: ${response.body.toString()}");
} on SocketException catch (e) { } on SocketException catch (e) {
final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr(); final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr();
onFailure(message, -1, failureType: ConnectivityFailure(message)); onFailure(message, -1, failureType: ConnectivityFailure(message));
_analytics.errorTracking.log(endPoint, error: "SocketException: $e"); _analytics.errorTracking.log(endPoint, error: "SocketException: $e");
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
// Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody));
Clarity.sendCustomEvent('api_failure');
return; return;
} on http.ClientException catch (e) { } on http.ClientException catch (e) {
final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr(); final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr();
@ -274,6 +280,10 @@ class ApiClientImp implements ApiClient {
final message = LocaleKeys.networkTimeout.tr(); final message = LocaleKeys.networkTimeout.tr();
onFailure(message, -1, failureType: ConnectivityFailure(message)); onFailure(message, -1, failureType: ConnectivityFailure(message));
_analytics.errorTracking.log(endPoint, error: "TimeoutException"); _analytics.errorTracking.log(endPoint, error: "TimeoutException");
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
// Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody));
Clarity.sendCustomEvent('api_timeout_failure');
return; return;
} catch (e) { } catch (e) {
final message = LocaleKeys.networkUnknownError.tr(); final message = LocaleKeys.networkUnknownError.tr();
@ -287,6 +297,11 @@ class ApiClientImp implements ApiClient {
if (statusCode < 200 || statusCode >= 400) { if (statusCode < 200 || statusCode >= 400) {
onFailure(LocaleKeys.networkErrorWhileFetching.tr(), statusCode, failureType: StatusCodeFailure(LocaleKeys.networkErrorWhileFetching.tr())); onFailure(LocaleKeys.networkErrorWhileFetching.tr(), statusCode, failureType: StatusCodeFailure(LocaleKeys.networkErrorWhileFetching.tr()));
logApiEndpointError(endPoint, LocaleKeys.networkErrorWhileFetching.tr(), statusCode); logApiEndpointError(endPoint, LocaleKeys.networkErrorWhileFetching.tr(), statusCode);
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
// Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody));
Clarity.setCustomTag('api_err_status', statusCode.toString());
Clarity.sendCustomEvent('api_status_code_failure');
} else { } else {
var parsed = json.decode(utf8.decode(response.bodyBytes)); var parsed = json.decode(utf8.decode(response.bodyBytes));
if (isAllowAny) { if (isAllowAny) {
@ -388,6 +403,11 @@ class ApiClientImp implements ApiClient {
); );
logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode); logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode);
} }
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
// Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody));
Clarity.setCustomTag('api_err_status', statusCode.toString());
Clarity.sendCustomEvent('api_message_status_failure');
} }
} else { } else {
if (parsed['SameClinicApptList'] != null) { if (parsed['SameClinicApptList'] != null) {
@ -666,4 +686,42 @@ class ApiClientImp implements ApiClient {
logApiEndpointError(String endPoint, error, code) { logApiEndpointError(String endPoint, error, code) {
_analytics.errorTracking.log(endPoint, error: error); _analytics.errorTracking.log(endPoint, error: error);
} }
String _getEndofUrl(String url) {
const int clarityLimit = 255;
if (url.length <= clarityLimit) return url;
// Extracts the final 252 characters and prepends '...'
return '...${url.substring(url.length - (clarityLimit - 3))}';
}
String _sanitizeRequestBody(String body) {
if (body.isEmpty) return '';
try {
// Handle standard JSON bodies
final Map<String, dynamic> data = json.decode(body);
// Case-insensitive key matching and removal
data.removeWhere((key, value) => key.toLowerCase() == 'sessionid' || key.toLowerCase() == 'tokenid');
return json.encode(data);
} catch (_) {
// Fallback: Handle Form URL Encoded strings or raw key-value pairs (SessionID=XYZ&TokenID=abc)
String modifiedBody = body;
final sensitiveKeys = ['SessionID', 'TokenID'];
for (var key in sensitiveKeys) {
// Matches "key=value" surrounded by standard query delimiters
final regExp = RegExp(r'' + key + r'=[^&]*&?', caseSensitive: false);
modifiedBody = modifiedBody.replaceAll(regExp, '');
}
// Clean up any trailing trailing ampersands
if (modifiedBody.endsWith('&')) {
modifiedBody = modifiedBody.substring(0, modifiedBody.length - 1);
}
return modifiedBody;
}
}
} }

@ -1,4 +1,5 @@
import 'package:amazon_payfort/amazon_payfort.dart'; import 'package:amazon_payfort/amazon_payfort.dart';
import 'package:flutter_amazonpaymentservices/environment_type.dart';
import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/enums.dart';
class ApiConsts { class ApiConsts {
@ -23,6 +24,7 @@ class ApiConsts {
static var payFortEnvironment = FortEnvironment.production; static var payFortEnvironment = FortEnvironment.production;
static var applePayMerchantId = "merchant.com.hmgwebservices"; static var applePayMerchantId = "merchant.com.hmgwebservices";
static var payFortEnvironmentType = EnvironmentType.production;
static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL LIVE static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL LIVE
// static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; // Payfort Payment Gateway URL UAT // static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; // Payfort Payment Gateway URL UAT
@ -47,6 +49,7 @@ class ApiConsts {
case AppEnvironmentTypeEnum.prod: case AppEnvironmentTypeEnum.prod:
baseUrl = "https://hmgwebservices.com/"; baseUrl = "https://hmgwebservices.com/";
payFortEnvironment = FortEnvironment.production; payFortEnvironment = FortEnvironment.production;
payFortEnvironmentType = EnvironmentType.production;
applePayMerchantId = "merchant.com.hmgwebservices"; applePayMerchantId = "merchant.com.hmgwebservices";
SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx";
TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout"; TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout";
@ -62,6 +65,7 @@ class ApiConsts {
case AppEnvironmentTypeEnum.dev: case AppEnvironmentTypeEnum.dev:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test; payFortEnvironment = FortEnvironment.test;
payFortEnvironmentType = EnvironmentType.sandbox;
applePayMerchantId = "merchant.com.hmgwebservices.uat"; applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx'; SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
@ -78,6 +82,7 @@ class ApiConsts {
case AppEnvironmentTypeEnum.uat: case AppEnvironmentTypeEnum.uat:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test; payFortEnvironment = FortEnvironment.test;
payFortEnvironmentType = EnvironmentType.sandbox;
applePayMerchantId = "merchant.com.hmgwebservices.uat"; applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx'; SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
@ -94,6 +99,7 @@ class ApiConsts {
case AppEnvironmentTypeEnum.preProd: case AppEnvironmentTypeEnum.preProd:
baseUrl = "https://webservices.hmg.com/"; baseUrl = "https://webservices.hmg.com/";
payFortEnvironment = FortEnvironment.production; payFortEnvironment = FortEnvironment.production;
payFortEnvironmentType = EnvironmentType.production;
applePayMerchantId = "merchant.com.hmgwebservices"; applePayMerchantId = "merchant.com.hmgwebservices";
SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx";
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
@ -110,6 +116,7 @@ class ApiConsts {
case AppEnvironmentTypeEnum.qa: case AppEnvironmentTypeEnum.qa:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test; payFortEnvironment = FortEnvironment.test;
payFortEnvironmentType = EnvironmentType.sandbox;
applePayMerchantId = "merchant.com.hmgwebservices.uat"; applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx'; SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
@ -126,6 +133,7 @@ class ApiConsts {
case AppEnvironmentTypeEnum.staging: case AppEnvironmentTypeEnum.staging:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test; payFortEnvironment = FortEnvironment.test;
payFortEnvironmentType = EnvironmentType.sandbox;
applePayMerchantId = "merchant.com.hmgwebservices.uat"; applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx'; SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";

@ -48,9 +48,16 @@ class AppState {
bool isPaytabsEnabled = false; bool isPaytabsEnabled = false;
bool isPayfortHostedPageEnabled = true;
void setIsPaytabsEnabled(bool value) { void setIsPaytabsEnabled(bool value) {
isPaytabsEnabled = value; // isPaytabsEnabled = value;
// isPaytabsEnabled = false; isPaytabsEnabled = false;
}
void setIsPayfortHostedPageEnabled(bool value) {
isPayfortHostedPageEnabled = value;
// isPayfortHostedPageEnabled = false;
} }
int? _superUserID; int? _superUserID;

@ -9,7 +9,7 @@ class DateUtil {
/// convert String To Date function /// convert String To Date function
/// [date] String we want to convert /// [date] String we want to convert
static DateTime convertStringToDate(String? date) { static DateTime convertStringToDate(String? date) {
if (date == null) return DateTime.now(); if (date == null || date == "null") return DateTime.now();
if (date.isEmpty) return DateTime.now(); if (date.isEmpty) return DateTime.now();
const start = "/Date("; const start = "/Date(";

@ -26,6 +26,7 @@ import 'package:hmg_patient_app_new/features/authentication/models/resp_models/a
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_activation_code_resp_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_activation_code_resp_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_user_staus_nhic_response_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_user_staus_nhic_response_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
@ -1222,6 +1223,7 @@ class AuthenticationViewModel extends ChangeNotifier {
_appState.setPrivilegeModelList(activation.list!.first.listPrivilege!); _appState.setPrivilegeModelList(activation.list!.first.listPrivilege!);
_appState.setUserBloodGroup = activation.patientBlodType ?? "N/A"; _appState.setUserBloodGroup = activation.patientBlodType ?? "N/A";
_appState.setIsPaytabsEnabled(Utils.havePrivilege(120)); _appState.setIsPaytabsEnabled(Utils.havePrivilege(120));
_appState.setIsPayfortHostedPageEnabled(Utils.havePrivilege(121));
// Refresh privileges after user switching to ensure correct privileges are loaded // Refresh privileges after user switching to ensure correct privileges are loaded
if (isSwitchUser) { if (isSwitchUser) {
@ -1252,6 +1254,7 @@ class AuthenticationViewModel extends ChangeNotifier {
} }
await clearDefaultInputValues(); await clearDefaultInputValues();
myAppointmentsVM.setIsAppointmentDataToBeLoaded(true); myAppointmentsVM.setIsAppointmentDataToBeLoaded(true);
getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
getIt.get<InsuranceViewModel>().setIsInsuranceDataToBeLoaded(true); getIt.get<InsuranceViewModel>().setIsInsuranceDataToBeLoaded(true);
getIt.get<HmgServicesViewModel>().setHasVitalSignDataLoaded(false); getIt.get<HmgServicesViewModel>().setHasVitalSignDataLoaded(false);
if (isUserAgreedBefore) { if (isUserAgreedBefore) {
@ -1765,6 +1768,7 @@ class AuthenticationViewModel extends ChangeNotifier {
}); });
_appState.setProjectsDetailList(projectDetailListModel); _appState.setProjectsDetailList(projectDetailListModel);
_appState.setIsPaytabsEnabled(Utils.havePrivilege(120)); _appState.setIsPaytabsEnabled(Utils.havePrivilege(120));
_appState.setIsPayfortHostedPageEnabled(Utils.havePrivilege(121));
} }
}, },
); );

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:clarity_flutter/clarity_flutter.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -139,6 +140,7 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
@override @override
void initState() { void initState() {
Clarity.setCurrentScreenName('OTP Verification Screen');
super.initState(); super.initState();
authVm = context.read<AuthenticationViewModel>(); authVm = context.read<AuthenticationViewModel>();
focusNode = widget.focusNode ?? FocusNode(); focusNode = widget.focusNode ?? FocusNode();

@ -347,7 +347,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
(failure) { (failure) {
patientHasAdvanceERBalance = false; patientHasAdvanceERBalance = false;
isERBookAppointment = true; isERBookAppointment = true;
isAdvanceERBalanceNeedToBeLoaded = true; isAdvanceERBalanceNeedToBeLoaded = false;
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(failure.message); onSuccess(failure.message);
} }
@ -357,7 +357,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
patientHasAdvanceERBalance = false; patientHasAdvanceERBalance = false;
isERBookAppointment = true; isERBookAppointment = true;
isAdvanceERBalanceNeedToBeLoaded = true; isAdvanceERBalanceNeedToBeLoaded = false;
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
patientHasAdvanceERBalance = apiResponse.data; patientHasAdvanceERBalance = apiResponse.data;
isERBookAppointment = !patientHasAdvanceERBalance; isERBookAppointment = !patientHasAdvanceERBalance;

@ -329,6 +329,7 @@ class HabibWalletViewModel extends ChangeNotifier {
isWalletAmountLoading = true; isWalletAmountLoading = true;
habibWalletAmount = 0; habibWalletAmount = 0;
isLakumAccountInfoLoading = true; isLakumAccountInfoLoading = true;
yahalaAccountNumber = "";
} }
isBottomSheetContentLoading = false; isBottomSheetContentLoading = false;
walletRechargeAmount = 0; walletRechargeAmount = 0;
@ -339,7 +340,6 @@ class HabibWalletViewModel extends ChangeNotifier {
fileNumber = ''; fileNumber = '';
depositorName = ''; depositorName = '';
mobileNumber = ''; mobileNumber = '';
yahalaAccountNumber = "";
notifyListeners(); notifyListeners();
} }
@ -551,7 +551,12 @@ class HabibWalletViewModel extends ChangeNotifier {
final result = await habibWalletRepo.getLakumAccountInformation(identificationNumber: getIt.get<AppState>().getAuthenticatedUser()!.patientIdentificationNo ?? ""); final result = await habibWalletRepo.getLakumAccountInformation(identificationNumber: getIt.get<AppState>().getAuthenticatedUser()!.patientIdentificationNo ?? "");
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure), // (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) async {
yahalaAccountNumber = "0";
isLakumAccountInfoLoading = false;
notifyListeners(); // 675534
},
(apiResponse) async { (apiResponse) async {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
yahalaAccountNumber = "0"; yahalaAccountNumber = "0";

@ -54,7 +54,8 @@ class AppointmentParkingQRResponseModel {
isUsed = json['IsUsed']; isUsed = json['IsUsed'];
patientID = json['PatientID']; patientID = json['PatientID'];
projectID = json['ProjectID']; projectID = json['ProjectID'];
ticketBase64 = json['TicketBase64'].cast<int>(); // ticketBase64 = json['TicketBase64'].cast<int>();
ticketBase64 = json['TicketBase64String'];
ticketID = json['TicketID']; ticketID = json['TicketID'];
ticketURL = json['TicketURL']; ticketURL = json['TicketURL'];
} }

@ -1,3 +1,6 @@
import 'dart:convert';
import 'dart:typed_data';
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:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
@ -103,6 +106,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
AppointmentParkingQRResponseModel? appointmentParkingQRResponseModel; AppointmentParkingQRResponseModel? appointmentParkingQRResponseModel;
Uint8List? parkingQRImageBytes;
MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService, required this.appState}); MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService, required this.appState});
void onTabChange(int index) { void onTabChange(int index) {
@ -736,6 +741,9 @@ class MyAppointmentsViewModel extends ChangeNotifier {
}, },
(apiResponse) { (apiResponse) {
appointmentParkingQRResponseModel = apiResponse.data!; appointmentParkingQRResponseModel = apiResponse.data!;
parkingQRImageBytes = base64Decode(
appointmentParkingQRResponseModel!.ticketBase64.contains(',') ? appointmentParkingQRResponseModel!.ticketBase64.split(',').last : appointmentParkingQRResponseModel!.ticketBase64);
// parkingQRImageBytes = appointmentParkingQRResponseModel!.ticketBase64;
notifyListeners(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);

@ -1,8 +1,14 @@
import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'dart:io';
import 'package:amazon_payfort/amazon_payfort.dart'; import 'package:amazon_payfort/amazon_payfort.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_amazonpaymentservices/flutter_amazonpaymentservices.dart';
import 'package:hmg_patient_app_new/core/api_consts.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/dependencies.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart'; import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/payfort_check_payment_status_response_model.dart'; import 'package:hmg_patient_app_new/features/payfort/models/payfort_check_payment_status_response_model.dart';
@ -12,6 +18,7 @@ import 'package:hmg_patient_app_new/features/payfort/payfort_repo.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/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:network_info_plus/network_info_plus.dart'; import 'package:network_info_plus/network_info_plus.dart';
import 'package:crypto/crypto.dart';
class PayfortViewModel extends ChangeNotifier { class PayfortViewModel extends ChangeNotifier {
PayfortRepo payfortRepo; PayfortRepo payfortRepo;
@ -27,6 +34,8 @@ class PayfortViewModel extends ChangeNotifier {
GetTamaraInstallmentsDetailsResponseModel? getTamaraInstallmentsDetailsResponseModel; GetTamaraInstallmentsDetailsResponseModel? getTamaraInstallmentsDetailsResponseModel;
bool isTamaraDetailsLoading = false; bool isTamaraDetailsLoading = false;
SdkTokenResponse? sdkTokenResponse;
PayfortViewModel({required this.payfortRepo, required this.errorHandlerService}); PayfortViewModel({required this.payfortRepo, required this.errorHandlerService});
setIsApplePayConfigurationLoading(bool value) { setIsApplePayConfigurationLoading(bool value) {
@ -142,6 +151,100 @@ class PayfortViewModel extends ChangeNotifier {
); );
} }
String buildManualPayfortSignature({
required String accessCode,
required String deviceId,
required String merchantIdentifier,
required String amount,
required String currency,
required String merchantReference,
required String shaRequestPhrase, // Your secret Sandbox phrase
}) {
// 1. Map all properties in strict ALPHABETICAL order by native key names
// Notice that "amount" and "currency" must come first alphabetically
final parameters = {
"access_code": accessCode,
"device_id": deviceId,
"language": "en",
"merchant_identifier": merchantIdentifier,
"service_command": "SDK_TOKEN",
};
// 2. Concatenate into a single continuous string wrapped in your SHA Phrase
StringBuffer buffer = StringBuffer();
buffer.write(shaRequestPhrase);
parameters.forEach((key, value) {
buffer.write('$key=$value');
});
buffer.write(shaRequestPhrase);
// 3. Convert string to SHA-256 hex signature
var bytes = utf8.encode(buffer.toString());
return sha256.convert(bytes).toString();
}
Future<SdkTokenResponse?> generateSdkResponseForNormalPayment({
String? accessCode,
String? merchantIdentifier,
String? shaType,
String? shaRequestPhrase,
String? merchantReference,
String? amount,
}) async {
SdkTokenResponse? sdkTokenResponse;
try {
// String? deviceId = await _payfort.getDeviceId();
String? deviceId = await FlutterAmazonpaymentservices.getUDID;
// String? deviceId = "79B8F1C1-F8BD-40A1-9FDD-C07B8AC44D17";
/// Step 2: Generate the Signature
SdkTokenRequest tokenRequest = SdkTokenRequest(
accessCode: accessCode!,
deviceId: deviceId ?? '',
merchantIdentifier: merchantIdentifier!,
);
// String? signature = await _payfort.generateSignature(
// shaType: shaType!,
// concatenatedString: tokenRequest.toConcatenatedString(shaRequestPhrase!),
// );
String? signature = buildManualPayfortSignature(
accessCode: accessCode,
deviceId: deviceId!,
merchantIdentifier: merchantIdentifier,
amount: "10000",
currency: "SAR",
merchantReference: merchantReference!,
shaRequestPhrase: shaRequestPhrase!,
);
tokenRequest = tokenRequest.copyWith(signature: signature);
this.sdkTokenResponse = SdkTokenResponse();
/// Step 3: Generate the SDK Token
final result = await payfortRepo.generateSdkSignatureFromAPI(tokenRequest: tokenRequest);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
// payfortProjectDetailsRespModel = apiResponse.data!;
sdkTokenResponse = apiResponse.data;
this.sdkTokenResponse = sdkTokenResponse;
notifyListeners();
}
},
);
} catch (e) {
log("Error here: ${e.toString()}");
}
return sdkTokenResponse;
}
Future<SdkTokenResponse?> _generateSdkResponse({ Future<SdkTokenResponse?> _generateSdkResponse({
String? applePayAccessCode, String? applePayAccessCode,
String? merchantIdentifier, String? merchantIdentifier,
@ -187,6 +290,74 @@ class PayfortViewModel extends ChangeNotifier {
return sdkTokenResponse; return sdkTokenResponse;
} }
Future<void> initiatePaymentWithCard( // var request, {
{
SucceededCallback? onSucceeded,
FailedCallback? onFailed,
String countryIsoCode = "SA",
String transactionID = "",
String amount = "0",
String fileNumber = "0",
}) async {
dynamic paymentResult;
try {
await generateSdkResponseForNormalPayment(
accessCode: payfortProjectDetailsRespModel!.accessCode,
merchantIdentifier: payfortProjectDetailsRespModel!.merchantIdentifier,
shaRequestPhrase: payfortProjectDetailsRespModel!.shaRequest,
shaType: "SHA-256",
merchantReference: transactionID,
amount: amount);
// Force a 100ms microtask buffer to isolate the UI rendering channel
await Future.delayed(const Duration(milliseconds: 350));
var request = {
"command": "PURCHASE",
"merchant_reference": transactionID, // Completely alphanumeric testing reference
"amount": Platform.isAndroid ? (num.parse(amount) * 100).toInt() : (num.parse(amount) * 100).toInt().toString(),
// "amount": "100000",
"currency": getIt.get<AppState>().getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
"language": "en",
"customer_email": "CustID_$fileNumber@HMG.com",
"sdk_token": sdkTokenResponse!.sdkToken,
"customer_name": "${getIt.get<AppState>().getAuthenticatedUser()!.firstName} ${getIt.get<AppState>().getAuthenticatedUser()!.lastName}",
};
LoaderBottomSheet.hideLoader();
paymentResult = await FlutterAmazonpaymentservices.normalPay(request, ApiConsts.payFortEnvironmentType, isShowResponsePage: false);
final paymentMap = Map<String, dynamic>.from(paymentResult);
String responseCode = paymentMap['response_code'] ?? "";
print("Payment Success Data: ${paymentMap.toString()}");
if (responseCode.startsWith("14") || responseCode == "20064") {
if (onSucceeded != null) {
onSucceeded(PayFortResult.fromMap(paymentMap));
}
} else if (responseCode == "13000") {
// Push user to 3D Secure Web View
} else {
// Treat as a payment rejection/technical failure
if (onFailed != null) {
onFailed(PayFortFailureResult.fromMap(paymentMap));
}
}
} on PlatformException catch (e) {
// If the error is the MethodChannel duplicate reply bug, handle it safely
if (e.message != null && e.message!.contains("Reply already submitted")) {
print("Swallowed native channel race-condition crash safely.");
return;
}
// Process real payment rejection errors here
print("Actual Payment Failure: ${e.details.toString()}");
} catch (error) {
print("Generic Error: $error");
}
}
Future<void> paymentWithApplePay({ Future<void> paymentWithApplePay({
SucceededCallback? onSucceeded, SucceededCallback? onSucceeded,
FailedCallback? onFailed, FailedCallback? onFailed,
@ -257,7 +428,6 @@ class PayfortViewModel extends ChangeNotifier {
onError!(failure.message); onError!(failure.message);
}, },
(apiResponse) { (apiResponse) {
log(apiResponse.data);
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);
} }

@ -1,6 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_paytabs_bridge/BaseBillingShippingInfo.dart'; import 'package:flutter_paytabs_bridge/BaseBillingShippingInfo.dart';
import 'package:flutter_paytabs_bridge/IOSThemeConfiguration.dart'; import 'package:flutter_paytabs_bridge/IOSThemeConfiguration.dart';
import 'package:flutter_paytabs_bridge/PaymentSDKNetworks.dart';
import 'package:flutter_paytabs_bridge/PaymentSdkApms.dart';
import 'package:flutter_paytabs_bridge/PaymentSdkConfigurationDetails.dart'; import 'package:flutter_paytabs_bridge/PaymentSdkConfigurationDetails.dart';
import 'package:flutter_paytabs_bridge/PaymentSdkLocale.dart'; import 'package:flutter_paytabs_bridge/PaymentSdkLocale.dart';
import 'package:flutter_paytabs_bridge/PaymentSdkTokenFormat.dart'; import 'package:flutter_paytabs_bridge/PaymentSdkTokenFormat.dart';
@ -95,6 +97,61 @@ class PayTabsViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setAlternatePaymentConfiguration(String paymentDescription, num amount, {bool isAddCard = false}) {
String cartID = DateTime.now().millisecondsSinceEpoch.toString();
paymentConfiguration = PaymentSdkConfigurationDetails(
profileId: PaymentSdkDefaultConfig.profileID,
serverKey: PaymentSdkDefaultConfig.serverKey,
clientKey: PaymentSdkDefaultConfig.clientKey,
transactionType: PaymentSdkTransactionType.SALE,
cartId: cartID.substring((cartID.length - 5), cartID.length),
cartDescription: paymentDescription,
merchantName: PaymentSdkDefaultConfig.defaultMerchantName,
locale: appState.isArabic() ? PaymentSdkLocale.AR : PaymentSdkLocale.EN,
screentTitle: paymentDescription,
amount: amount.toDouble(),
showBillingInfo: false,
forceShippingInfo: false,
currencyCode: PaymentSdkDefaultConfig.defaultCurrency,
merchantCountryCode: PaymentSdkDefaultConfig.defaultMerchantCountryCode,
billingDetails: BillingDetails("${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" ?? "HMG Patient",
"CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com", appState.getAuthenticatedUser()!.mobileNumber ?? "0000000000", "Riyadh", "SA", "Riyadh", "Riyadh", "12626"),
shippingDetails: ShippingDetails("${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" ?? "HMG Patient",
"CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com", appState.getAuthenticatedUser()!.mobileNumber ?? "0000000000", "Riyadh", "SA", "Riyadh", "Riyadh", "12626"),
alternativePaymentMethods: [PaymentSdkAPms.TAMARA],
linkBillingNameWithCardHolderName: false,
simplifyApplePayValidation: true,
);
paymentConfiguration.iOSThemeConfigurations = IOSThemeConfigurations(
// logoImage: "assets/images/png/hmg_logo.png",
backgroundColor: "ffffff",
backgroundColorDark: "ffffff",
secondaryColor: "191919",
secondaryColorDark: "191919",
primaryColor: "ffffff",
primaryColorDark: "ffffff",
// primaryFont: "Poppins",
titleFontColor: "2E3039",
titleFontColorDark: "2E3039",
// titleFont: "Poppins",
primaryFontColor: "2E3039",
primaryFontColorDark: "2E3039",
inputFieldBackgroundColor: "ffffff",
inputFieldBackgroundColorDark: "ffffff",
placeholderColor: "2E3039",
placeholderColorDark: "2E3039",
buttonColor: "ED1C2B",
buttonColorDark: "ED1C2B",
inputsCornerRadius: 12
// buttonFont: "Poppins",
);
paymentConfiguration.tokeniseType = PaymentSdkTokeniseType.USER_OPTIONAL_DEFAULT_ON;
paymentConfiguration.tokenFormat = PaymentSdkTokenFormat.Hex32Format;
notifyListeners();
}
startApplePayPayment({Function(PaytabsTransactionResponseModel)? onSuccess, Function(String)? onError}) async { startApplePayPayment({Function(PaytabsTransactionResponseModel)? onSuccess, Function(String)? onError}) async {
bool _callbackFired = false; // prevent duplicate callback invocations bool _callbackFired = false; // prevent duplicate callback invocations
@ -168,4 +225,39 @@ class PayTabsViewModel extends ChangeNotifier {
} }
}); });
} }
startAlternatePayment({Function(PaytabsTransactionResponseModel)? onSuccess, Function(String)? onError}) {
bool _callbackFired = false; // prevent duplicate callback invocations
FlutterPaytabsBridge.startAlternativePaymentMethod(paymentConfiguration, (event) {
if (_callbackFired) return; // ignore any subsequent callback calls
final transactionDetails = event["data"] as Map<Object?, Object?>?;
if (event["status"] == "success") {
_callbackFired = true;
// Cast Map<Object?, Object?> to Map<String, dynamic>
final data = Map<String, dynamic>.from(event["data"] as Map);
paytabsTransactionResponseModel = PaytabsTransactionResponseModel.fromJson(data);
if (paytabsTransactionResponseModel.isSuccess!) {
onSuccess!(paytabsTransactionResponseModel);
} else {
// Transaction was processed but failed
final reason = transactionDetails?["payResponseReturn"] ?? transactionDetails?["responseMessage"] ?? transactionDetails?["message"] ?? "Unknown error";
final responseCode = transactionDetails?["responseCode"] ?? "";
final errorMessage = responseCode.toString().isNotEmpty ? "Transaction failed (Code: $responseCode): $reason" : "Transaction failed: $reason";
onError!(errorMessage);
}
} else if (event["status"] == "error") {
_callbackFired = true;
final errorMessage = event["message"] ?? "An error occurred";
debugPrint("Error occurred in transaction: $errorMessage");
debugPrint("Full error event: $event");
onError!(errorMessage);
} else if (event["status"] == "event") {
// Intermediate SDK events do NOT mark _callbackFired, just log
final eventMessage = event["message"] ?? "Event occurred";
debugPrint("Event occurred: $eventMessage");
}
});
}
} }

@ -78,6 +78,7 @@ class AncillaryOrderItem {
int? orderNo; int? orderNo;
String? projectName; // Added from parent AncillaryOrderGroup String? projectName; // Added from parent AncillaryOrderGroup
int? projectID; // Added from parent AncillaryOrderGroup int? projectID; // Added from parent AncillaryOrderGroup
String? setupID; // Added from parent AncillaryOrderGroup
AncillaryOrderItem({ AncillaryOrderItem({
this.ancillaryProcedureListModels, this.ancillaryProcedureListModels,
@ -94,9 +95,10 @@ class AncillaryOrderItem {
this.orderNo, this.orderNo,
this.projectName, this.projectName,
this.projectID, this.projectID,
this.setupID,
}); });
factory AncillaryOrderItem.fromJson(Map<String, dynamic> json, {String? projectName, int? projectID}) => AncillaryOrderItem( factory AncillaryOrderItem.fromJson(Map<String, dynamic> json, {String? projectName, int? projectID, String? setupID}) => AncillaryOrderItem(
ancillaryProcedureListModels: json['AncillaryProcedureListModels'], ancillaryProcedureListModels: json['AncillaryProcedureListModels'],
appointmentDate: DateUtil.convertStringToDate(json['AppointmentDate']), appointmentDate: DateUtil.convertStringToDate(json['AppointmentDate']),
appointmentNo: json['AppointmentNo'] as int?, appointmentNo: json['AppointmentNo'] as int?,
@ -111,5 +113,6 @@ class AncillaryOrderItem {
orderNo: json['OrderNo'] as int?, orderNo: json['OrderNo'] as int?,
projectName: projectName, projectName: projectName,
projectID: projectID, projectID: projectID,
setupID: setupID,
); );
} }

@ -20,7 +20,7 @@ abstract class TodoSectionRepo {
Future<Either<Failure, dynamic>> createAdvancePayment({ Future<Either<Failure, dynamic>> createAdvancePayment({
required int projectID, required int projectID,
required double paymentAmount, required num paymentAmount,
required String paymentReference, required String paymentReference,
required String paymentMethodName, required String paymentMethodName,
required int patientTypeID, required int patientTypeID,
@ -85,11 +85,13 @@ class TodoSectionRepoImp implements TodoSectionRepo {
final ordersList = group['AncillaryOrderList'] as List; final ordersList = group['AncillaryOrderList'] as List;
final projectName = group['ProjectName'] as String?; final projectName = group['ProjectName'] as String?;
final projectID = group['ProjectID'] as int?; final projectID = group['ProjectID'] as int?;
final setupID = group['SetupID'] as String?;
// Parse each order item in the group // Parse each order item in the group
for (var orderJson in ordersList) { for (var orderJson in ordersList) {
if (orderJson is Map<String, dynamic>) { if (orderJson is Map<String, dynamic>) {
ancillaryOrders.add(AncillaryOrderItem.fromJson(orderJson, projectName: projectName, projectID: projectID)); ancillaryOrders.add(AncillaryOrderItem.fromJson(orderJson, projectName: projectName, projectID: projectID, setupID: setupID));
} }
} }
} }
@ -205,7 +207,7 @@ class TodoSectionRepoImp implements TodoSectionRepo {
@override @override
Future<Either<Failure, dynamic>> createAdvancePayment({ Future<Either<Failure, dynamic>> createAdvancePayment({
required int projectID, required int projectID,
required double paymentAmount, required num paymentAmount,
required String paymentReference, required String paymentReference,
required String paymentMethodName, required String paymentMethodName,
required int patientTypeID, required int patientTypeID,

@ -158,7 +158,7 @@ class TodoSectionViewModel extends ChangeNotifier {
Future<void> createAdvancePayment({ Future<void> createAdvancePayment({
required int projectID, required int projectID,
required double paymentAmount, required num paymentAmount,
required String paymentReference, required String paymentReference,
required String paymentMethodName, required String paymentMethodName,
required int patientTypeID, required int patientTypeID,

@ -1907,10 +1907,6 @@ abstract class LocaleKeys {
static const noProceduresAvailableForRefund = 'noProceduresAvailableForRefund'; static const noProceduresAvailableForRefund = 'noProceduresAvailableForRefund';
static const selectAll = 'selectAll'; static const selectAll = 'selectAll';
static const selectRefundMethod = 'selectRefundMethod'; static const selectRefundMethod = 'selectRefundMethod';
static const beforeUsingSymptomsChecker = 'beforeUsingSymptomsChecker';
static const symptomsCheckerNotDiagnosis = 'symptomsCheckerNotDiagnosis';
static const symptomsCheckerNotEmergency = 'symptomsCheckerNotEmergency';
static const yourDataIsSafe = 'yourDataIsSafe';
static const hmgWallet = 'hmgWallet'; static const hmgWallet = 'hmgWallet';
static const bankTransfer = 'bankTransfer'; static const bankTransfer = 'bankTransfer';
static const selectRefundReason = 'selectRefundReason'; static const selectRefundReason = 'selectRefundReason';
@ -1923,6 +1919,10 @@ abstract class LocaleKeys {
static const refundReason6 = 'refundReason6'; static const refundReason6 = 'refundReason6';
static const refundReason7 = 'refundReason7'; static const refundReason7 = 'refundReason7';
static const selectProcedures = 'selectProcedures'; static const selectProcedures = 'selectProcedures';
static const beforeUsingSymptomsChecker = 'beforeUsingSymptomsChecker';
static const symptomsCheckerNotDiagnosis = 'symptomsCheckerNotDiagnosis';
static const symptomsCheckerNotEmergency = 'symptomsCheckerNotEmergency';
static const yourDataIsSafe = 'yourDataIsSafe';
static const redeem = 'redeem'; static const redeem = 'redeem';
static const points = 'points'; static const points = 'points';
static const transactions = 'transactions'; static const transactions = 'transactions';
@ -1938,5 +1938,7 @@ abstract class LocaleKeys {
static const amountOnly = 'amountOnly'; static const amountOnly = 'amountOnly';
static const invoiceHistory = 'invoiceHistory'; static const invoiceHistory = 'invoiceHistory';
static const refund = 'refund'; static const refund = 'refund';
static const parkingQR = 'parkingQR';
static const parkingQRDesc = 'parkingQRDesc';
} }

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:clarity_flutter/clarity_flutter.dart';
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/api_consts.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart';
@ -85,6 +86,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
@override @override
void initState() { void initState() {
Clarity.setCurrentScreenName('Appointment Details Page');
super.initState(); super.initState();
scheduleMicrotask(() async { scheduleMicrotask(() async {
@ -284,7 +286,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
AppointmentDoctorCard( AppointmentDoctorCard(
// renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) || // renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ||
renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) || renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) ||
!Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)), !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID ?? 0)),
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));
@ -575,7 +577,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
iconColor: AppColors.transparent, iconColor: AppColors.transparent,
fit: BoxFit.cover, fit: BoxFit.cover,
width: double.infinity, width: double.infinity,
), height: 180.h),
), ),
), ),
Positioned( Positioned(
@ -875,35 +877,73 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
} }
}) })
: SizedBox.shrink(), : SizedBox.shrink(),
// (widget.patientAppointmentHistoryResponseModel.isParkingAvailable ?? false) (widget.patientAppointmentHistoryResponseModel.isParkingAvailable ?? false)
// ? MedicalFileCard( ? MedicalFileCard(
// label: LocaleKeys.getParkingQR.tr(context: context), label: LocaleKeys.getParkingQR.tr(context: context),
// textColor: AppColors.blackColor, textColor: AppColors.blackColor,
// backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,
// svgIcon: AppAssets.appointment_parking_icon, svgIcon: AppAssets.appointment_parking_icon,
// isLargeText: true, isLargeText: true,
// iconSize: 36.w, iconSize: 36.w,
// ).onPress(() { ).onPress(() {
// LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context)); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context));
// myAppointmentsViewModel.getParkingQR( myAppointmentsViewModel.getParkingQR(
// widget.patientAppointmentHistoryResponseModel.appointmentNo, widget.patientAppointmentHistoryResponseModel.appointmentNo,
// widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.projectID,
// onSuccess: (val) { onSuccess: (val) {
// LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
// }, showCommonBottomSheetWithoutHeight(
// onError: (err) { title: LocaleKeys.parkingQR.tr(context: context),
// LoaderBottomSheet.hideLoader(); context,
// showCommonBottomSheetWithoutHeight( child: Column(
// context, children: [
// child: Utils.getErrorWidget(loadingText: err), LocaleKeys.parkingQRDesc.tr(context: context).toText16(),
// callBackFunc: () {}, SizedBox(height: 16.h),
// isFullScreen: false, Image.memory(
// isCloseButtonVisible: true, myAppointmentsViewModel.parkingQRImageBytes!,
// ); width: 156.w,
// }, height: 156.h,
// ); fit: BoxFit.cover,
// }) gaplessPlayback: true, // Prevents blink during image rebuild
// : SizedBox.shrink(), ),
SizedBox(height: 16.h),
CustomButton(
text: LocaleKeys.done.tr(context: context),
onPressed: () {
openDoctorScheduleCalendar();
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: Colors.white,
fontSize: 16.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 50.h,
icon: AppAssets.checkmark_icon,
iconColor: Colors.white,
iconSize: 18.h,
).paddingSymmetrical(0.h, 0.h),
],
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
})
: SizedBox.shrink(),
], ],
); );
}), }),
@ -1272,6 +1312,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
case 50: case 50:
// return LocaleKeys.confirmLiveCare.tr(context: context); // return LocaleKeys.confirmLiveCare.tr(context: context);
case 90: case 90:
Clarity.setCurrentScreenName('Appointment CheckIn Page');
showCommonBottomSheetWithoutHeight(context, showCommonBottomSheetWithoutHeight(context,
title: LocaleKeys.onlineCheckIn.tr(context: context), title: LocaleKeys.onlineCheckIn.tr(context: context),
child: isAppointmentWithin4Hours child: isAppointmentWithin4Hours
@ -1281,9 +1322,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
) )
: Utils.getOnlineCheckInWidget( : Utils.getOnlineCheckInWidget(
loadingText: LocaleKeys.onlineCheckIn24HoursOnly.tr(context: context), loadingText: LocaleKeys.onlineCheckIn24HoursOnly.tr(context: context),
), ), callBackFunc: () {
callBackFunc: () {}, Clarity.setCurrentScreenName('Appointment Details Page');
isFullScreen: false); }, isFullScreen: false);
default: default:
// return "No Action".needTranslation; // return "No Action".needTranslation;
} }

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:developer'; import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:clarity_flutter/clarity_flutter.dart';
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';
@ -13,6 +14,7 @@ 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';
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/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';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart'; import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart';
@ -59,6 +61,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
@override @override
void initState() { void initState() {
scheduleMicrotask(() { scheduleMicrotask(() {
Clarity.setCurrentScreenName('Appointment Payment Page');
payfortViewModel.initPayfortViewModel(); payfortViewModel.initPayfortViewModel();
payfortViewModel.setIsApplePayConfigurationLoading(false); payfortViewModel.setIsApplePayConfigurationLoading(false);
myAppointmentsViewModel.getPatientShareAppointment( myAppointmentsViewModel.getPatientShareAppointment(
@ -112,8 +115,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h) Utils.buildImgWithAssets(icon: AppAssets.mada, width: 80.h, height: 30.h, fit: BoxFit.contain).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
.toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
], ],
@ -132,7 +134,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
), ),
], ],
).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() async {
selectedPaymentMethod = "MADA"; selectedPaymentMethod = "MADA";
if (appState.isPaytabsEnabled) { if (appState.isPaytabsEnabled) {
paytabsViewModel.setPaymentConfiguration( paytabsViewModel.setPaymentConfiguration(
@ -212,7 +214,100 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
); );
}); });
} else { } else {
openPaymentURL("mada"); if (appState.isPayfortHostedPageEnabled) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPaymentInformation.tr(context: context));
transID = Utils.getAppointmentTransID(
widget.patientAppointmentHistoryResponseModel.projectID,
widget.patientAppointmentHistoryResponseModel.clinicID,
widget.patientAppointmentHistoryResponseModel.appointmentNo,
);
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(), projectId: widget.patientAppointmentHistoryResponseModel.projectID, integrationId: 1);
payfortViewModel.initiatePaymentWithCard(
transactionID: transID,
fileNumber: appState.getAuthenticatedUser()!.patientId.toString(),
amount: myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax!.toString(),
onSucceeded: (paymentResult) async {
print("Payment Success Data: ${paymentResult.toString()}");
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr(context: context));
await myAppointmentsViewModel.createAdvancePayment(
paymentMethodName: selectedPaymentMethod,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
payedAmount: num.parse(myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax!.toString()),
paymentReference: paymentResult.fortId!,
patientID: appState.getAuthenticatedUser()!.patientId.toString(),
patientType: appState.getAuthenticatedUser()!.patientType!,
onSuccess: (value) async {
print(value);
await myAppointmentsViewModel.addAdvanceNumberRequest(
advanceNumber: Utils.isVidaPlusProject(widget.patientAppointmentHistoryResponseModel.projectID)
? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString()
: value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(),
paymentReference: paymentResult.fortId!,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
onSuccess: (value) async {
if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) {
//TODO: Implement LiveCare Check-In API Call
await myAppointmentsViewModel.insertLiveCareVIDARequest(
clientRequestID: transID,
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
});
},
onError: (error) {});
} else {
await myAppointmentsViewModel.generateAppointmentQR(
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!,
onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
});
});
}
});
});
},
onFailed: (err) {
print("Payment Failed Data: ${err.toString()}");
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
} else {
openPaymentURL("mada");
}
} }
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -232,7 +327,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
children: [ children: [
Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h), Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h),
SizedBox(width: 8.h), SizedBox(width: 8.h),
Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h), Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h, fit: BoxFit.contain),
], ],
).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -255,7 +350,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
), ),
], ],
).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() async {
selectedPaymentMethod = "VISA"; selectedPaymentMethod = "VISA";
if (appState.isPaytabsEnabled) { if (appState.isPaytabsEnabled) {
paytabsViewModel.setPaymentConfiguration( paytabsViewModel.setPaymentConfiguration(
@ -335,7 +430,110 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
); );
}); });
} else { } else {
openPaymentURL("visa"); if (appState.isPayfortHostedPageEnabled) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPaymentInformation.tr(context: context));
transID = Utils.getAppointmentTransID(
widget.patientAppointmentHistoryResponseModel.projectID,
widget.patientAppointmentHistoryResponseModel.clinicID,
widget.patientAppointmentHistoryResponseModel.appointmentNo,
);
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(), projectId: widget.patientAppointmentHistoryResponseModel.projectID, integrationId: 1);
payfortViewModel.initiatePaymentWithCard(
transactionID: transID,
fileNumber: appState.getAuthenticatedUser()!.patientId.toString(),
amount: myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax!.toString(),
onSucceeded: (paymentResult) async {
print("Payment Success Data: ${paymentResult.toString()}");
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr(context: context));
await myAppointmentsViewModel.createAdvancePayment(
paymentMethodName: selectedPaymentMethod,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
payedAmount: num.parse(myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax!.toString()),
paymentReference: paymentResult.fortId!,
patientID: appState.getAuthenticatedUser()!.patientId.toString(),
patientType: appState.getAuthenticatedUser()!.patientType!,
onSuccess: (value) async {
print(value);
await myAppointmentsViewModel.addAdvanceNumberRequest(
advanceNumber: Utils.isVidaPlusProject(widget.patientAppointmentHistoryResponseModel.projectID)
? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString()
: value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(),
paymentReference: paymentResult.fortId!,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
onSuccess: (value) async {
if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) {
//TODO: Implement LiveCare Check-In API Call
await myAppointmentsViewModel.insertLiveCareVIDARequest(
clientRequestID: transID,
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
});
},
onError: (error) {});
} else {
await myAppointmentsViewModel.generateAppointmentQR(
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!,
onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
});
});
}
});
});
},
onFailed: (err) {
print("Payment Failed Data: ${err.toString()}");
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
} else {
openPaymentURL("visa");
}
} }
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -376,7 +574,108 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "TAMARA"; selectedPaymentMethod = "TAMARA";
openPaymentURL("tamara"); if (appState.isPaytabsEnabled) {
paytabsViewModel.setAlternatePaymentConfiguration(
"Appointment Payment", num.parse(myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax!.toString()));
paytabsViewModel.startAlternatePayment(onSuccess: (PaytabsTransactionResponseModel transactionData) async {
tamaraOrderID = transactionData.cartID!;
transID = transactionData.transactionReference!;
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr(context: context));
await payfortViewModel.updateTamaraRequestStatus(responseMessage: "success", status: "14", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
await payfortViewModel.markAppointmentAsTamaraPaid(
projectID: widget.patientAppointmentHistoryResponseModel.projectID, appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo);
await myAppointmentsViewModel.createAdvancePayment(
paymentMethodName: selectedPaymentMethod,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
payedAmount: num.parse(myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax!.toString()),
paymentReference: transactionData.transactionReference!,
patientID: appState.getAuthenticatedUser()!.patientId.toString(),
patientType: appState.getAuthenticatedUser()!.patientType!,
onSuccess: (value) async {
print(value);
await myAppointmentsViewModel.addAdvanceNumberRequest(
advanceNumber: "Tamara-Advance-0000",
paymentReference: transactionData.transactionReference!,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
onSuccess: (value) async {
if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) {
//TODO: Implement LiveCare Check-In API Call
await myAppointmentsViewModel.insertLiveCareVIDARequest(
clientRequestID: transID,
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
LoaderBottomSheet.hideLoader();
getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
});
},
onError: (error) {});
} else {
await myAppointmentsViewModel.generateAppointmentQR(
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!,
onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
});
});
}
});
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}, onError: (err) async {
await payfortViewModel.updateTamaraRequestStatus(responseMessage: "Failed", status: "00", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
} else {
openPaymentURL("tamara");
}
}) })
: SizedBox.shrink(), : SizedBox.shrink(),
], ],
@ -443,21 +742,24 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
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((myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare ?? 0).toString().toText16(isBold: true), AppColors.blackColor, ClarityUnmask(
13, child: Utils.getPaymentAmountWithSymbol(
isSaudiCurrency: true), (myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare ?? 0).toString().toText16(isBold: true), AppColors.blackColor, 13,
isSaudiCurrency: true),
),
], ],
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
"VAT 15%".toText14(isBold: true, color: AppColors.greyTextColor), "VAT 15%".toText14(isBold: true, color: AppColors.greyTextColor),
Utils.getPaymentAmountWithSymbol( ClarityUnmask(
(myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount ?? 0).toString() child: Utils.getPaymentAmountWithSymbol(
.toText14(isBold: true, color: AppColors.greyTextColor), (myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount ?? 0).toString().toText14(isBold: true, color: AppColors.greyTextColor),
AppColors.greyTextColor, AppColors.greyTextColor,
13, 13,
isSaudiCurrency: true), isSaudiCurrency: true),
),
], ],
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 17.h), SizedBox(height: 17.h),
@ -465,10 +767,11 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
"".toText14(isBold: true), "".toText14(isBold: true),
Utils.getPaymentAmountWithSymbol( ClarityUnmask(
(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax ?? 0).toString().toText24(isBold: true), AppColors.blackColor, child: Utils.getPaymentAmountWithSymbol(
17, (myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax ?? 0).toString().toText24(isBold: true), AppColors.blackColor, 17,
isSaudiCurrency: true), isSaudiCurrency: true),
),
], ],
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),
Platform.isIOS Platform.isIOS
@ -566,6 +869,11 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false); myAppointmentsViewModel.getPatientAppointments(true, false);
getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,
@ -587,6 +895,11 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false); myAppointmentsViewModel.getPatientAppointments(true, false);
getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,
@ -654,6 +967,11 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false); myAppointmentsViewModel.getPatientAppointments(true, false);
getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,
@ -675,6 +993,11 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false); myAppointmentsViewModel.getPatientAppointments(true, false);
getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,
@ -831,6 +1154,11 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false); myAppointmentsViewModel.getPatientAppointments(true, false);
getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,
@ -852,6 +1180,11 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false); myAppointmentsViewModel.getPatientAppointments(true, false);
getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:clarity_flutter/clarity_flutter.dart';
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';
@ -311,16 +312,18 @@ class _AppointmentCardState extends State<AppointmentCard> {
? '${(widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital").substring(0, 15)}...' ? '${(widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital").substring(0, 15)}...'
: widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital") : widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital")
.toShimmer2(isShow: widget.isLoading), .toShimmer2(isShow: widget.isLoading),
Directionality( ClarityUnmask(
textDirection: ui.TextDirection.ltr, child: Directionality(
child: AppCustomChipWidget( textDirection: ui.TextDirection.ltr,
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), child: AppCustomChipWidget(
icon: AppAssets.appointment_calendar_icon, labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w),
richText: widget.isLoading icon: AppAssets.appointment_calendar_icon,
? 'Cardiology'.toText10().toShimmer2(isShow: widget.isLoading) richText: widget.isLoading
: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)}" ? 'Cardiology'.toText10().toShimmer2(isShow: widget.isLoading)
.toText10(isEnglishOnly: true, isBold: true), : "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)}"
).toShimmer2(isShow: widget.isLoading), .toText10(isEnglishOnly: true, isBold: true),
).toShimmer2(isShow: widget.isLoading),
),
), ),
// AppCustomChipWidget( // AppCustomChipWidget(

@ -1,5 +1,6 @@
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:clarity_flutter/clarity_flutter.dart';
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';
@ -135,17 +136,18 @@ class AppointmentDoctorCard extends StatelessWidget {
labelText: patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital", labelText: patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital",
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
), ),
Directionality( ClarityUnmask(
textDirection: ui.TextDirection.ltr, child: Directionality(
child: AppCustomChipWidget( textDirection: ui.TextDirection.ltr,
labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w), child: AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon, labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w),
richText: icon: AppAssets.doctor_calendar_icon,
"${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang( richText: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(
DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate),
false, false,
)}" )}"
.toText10(isBold: true), .toText10(isBold: true),
),
), ),
), ),
AppCustomChipWidget( AppCustomChipWidget(

@ -98,7 +98,7 @@ class DoctorResponseTransactions extends StatelessWidget {
color: AppColors.infoColor.withAlpha(20), color: AppColors.infoColor.withAlpha(20),
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
), ),
child: doctorResponseModel.requestTypeDescription!.toText12(color: AppColors.infoColor, isBold: true), child: (doctorResponseModel.requestTypeDescription ?? "").toText12(color: AppColors.infoColor, isBold: true),
), ),
], ],
), ),

@ -1,3 +1,4 @@
import 'package:clarity_flutter/clarity_flutter.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -32,6 +33,7 @@ class LoginScreenState extends State<LoginScreen> {
@override @override
void initState() { void initState() {
Clarity.setCurrentScreenName('Login Screen');
super.initState(); super.initState();
_nationalIdFocusNode = FocusNode(); _nationalIdFocusNode = FocusNode();
@ -97,7 +99,7 @@ class LoginScreenState extends State<LoginScreen> {
hintText: "xxxxxxxxx", hintText: "xxxxxxxxx",
controller: authVm.nationalIdController, controller: authVm.nationalIdController,
focusNode: _nationalIdFocusNode, focusNode: _nationalIdFocusNode,
keyboardType: TextInputType.text, keyboardType: TextInputType.number,
isEnable: true, isEnable: true,
prefix: null, prefix: null,
autoFocus: true, autoFocus: true,

@ -1,5 +1,6 @@
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:clarity_flutter/clarity_flutter.dart';
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';
@ -37,6 +38,7 @@ class _SavedLogin extends State<SavedLogin> {
@override @override
void initState() { void initState() {
Clarity.setCurrentScreenName('Last Login Screen');
authVm = context.read<AuthenticationViewModel>(); authVm = context.read<AuthenticationViewModel>();
appState = getIt.get<AppState>(); appState = getIt.get<AppState>();
loginType = LoginTypeExtension.fromValue(appState.getSelectDeviceByImeiRespModelElement!.logInType!)!; loginType = LoginTypeExtension.fromValue(appState.getSelectDeviceByImeiRespModelElement!.logInType!)!;
@ -58,7 +60,6 @@ class _SavedLogin extends State<SavedLogin> {
if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
authVm.loginWithFingerPrintFace(() {}); authVm.loginWithFingerPrintFace(() {});
} }
super.initState(); super.initState();
} }

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:clarity_flutter/clarity_flutter.dart';
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:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
@ -57,6 +58,7 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
@override @override
void initState() { void initState() {
Clarity.setCurrentScreenName('Book Appointment Page');
authVM = context.read<AuthenticationViewModel>(); authVM = context.read<AuthenticationViewModel>();
scheduleMicrotask(() { scheduleMicrotask(() {
// bookAppointmentsViewModel.selectedTabIndex = 0; // bookAppointmentsViewModel.selectedTabIndex = 0;

@ -86,8 +86,8 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
AppCustomChipWidget( AppCustomChipWidget(
richText: Row( richText: Row(
children: [ children: [
"${appState.getAuthenticatedUser()!.age} ".toText10(color: AppColors.blackColor, isEnglishOnly: true), "${appState.getAuthenticatedUser()!.age} ".toText10(color: AppColors.blackColor, isBold: true),
LocaleKeys.yearsOld.tr(context: context).toText10(color: AppColors.blackColor), LocaleKeys.yearsOld.tr(context: context).toText10(color: AppColors.blackColor, isBold: true),
], ],
), ),
), ),

@ -106,7 +106,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h), Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h, fit: BoxFit.contain),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true), LocaleKeys.mada.tr(context: context).toText16(isBold: true),
], ],
@ -169,7 +169,66 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
); );
}); });
} else { } else {
openPaymentURL("mada"); if (appState.isPayfortHostedPageEnabled) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPaymentInformation.tr(context: context));
transID = Utils.getAppointmentTransID(
immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!,
ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12,
DateTime.now().millisecondsSinceEpoch,
);
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.liveCareAppointment.getIdFromServiceEnum(),
projectId: ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12,
integrationId: 1);
payfortViewModel.initiatePaymentWithCard(
transactionID: transID,
fileNumber: appState.getAuthenticatedUser()!.patientId.toString(),
amount: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total ?? "0.0",
onSucceeded: (paymentResult) async {
print("Payment Success Data: ${paymentResult.toString()}");
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr(context: context));
await immediateLiveCareViewModel.addNewCallRequestForImmediateLiveCare(transID);
await immediateLiveCareViewModel.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader();
if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) {
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(),
),
);
} else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Unknown error occurred..."),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
},
onFailed: (err) {
print("Payment Failed Data: ${err.toString()}");
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
} else {
openPaymentURL("mada");
}
} }
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -189,7 +248,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
children: [ children: [
Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h), Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h),
SizedBox(width: 8.h), SizedBox(width: 8.h),
Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h), Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h, fit: BoxFit.contain),
], ],
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -210,7 +269,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
), ),
], ],
).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() async {
selectedPaymentMethod = "VISA"; selectedPaymentMethod = "VISA";
if (appState.isPaytabsEnabled) { if (appState.isPaytabsEnabled) {
paytabsViewModel.setPaymentConfiguration( paytabsViewModel.setPaymentConfiguration(
@ -254,7 +313,66 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
); );
}); });
} else { } else {
openPaymentURL("visa"); if (appState.isPayfortHostedPageEnabled) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPaymentInformation.tr(context: context));
transID = Utils.getAppointmentTransID(
immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!,
ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12,
DateTime.now().millisecondsSinceEpoch,
);
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.liveCareAppointment.getIdFromServiceEnum(),
projectId: ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12,
integrationId: 1);
payfortViewModel.initiatePaymentWithCard(
transactionID: transID,
fileNumber: appState.getAuthenticatedUser()!.patientId.toString(),
amount: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total ?? "0.0",
onSucceeded: (paymentResult) async {
print("Payment Success Data: ${paymentResult.toString()}");
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr(context: context));
await immediateLiveCareViewModel.addNewCallRequestForImmediateLiveCare(transID);
await immediateLiveCareViewModel.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader();
if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) {
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(),
),
);
} else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Unknown error occurred..."),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
},
onFailed: (err) {
print("Payment Failed Data: ${err.toString()}");
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
} else {
openPaymentURL("visa");
}
} }
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -584,7 +702,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
ApplePayInsertRequest applePayInsertRequest = ApplePayInsertRequest(); ApplePayInsertRequest applePayInsertRequest = ApplePayInsertRequest();
await payfortViewModel.getPayfortConfigurations( await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(), projectId: ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12, integrationId: 2); serviceId: ServiceTypeEnum.liveCareAppointment.getIdFromServiceEnum(), projectId: ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12, integrationId: 2);
applePayInsertRequest.clientRequestID = transID; applePayInsertRequest.clientRequestID = transID;
applePayInsertRequest.clinicID = immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!; applePayInsertRequest.clinicID = immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!;

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:clarity_flutter/clarity_flutter.dart';
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:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
@ -48,6 +49,12 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
Uint8List? _cachedImageBytes; Uint8List? _cachedImageBytes;
String? _cachedImageDataHash; String? _cachedImageDataHash;
@override
void initState() {
Clarity.setCurrentScreenName('Appointment Review Page');
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);

@ -1,3 +1,4 @@
import 'package:clarity_flutter/clarity_flutter.dart';
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:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
@ -39,6 +40,7 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
@override @override
void initState() { void initState() {
Clarity.setCurrentScreenName('Search By Doctor Name Page');
_scrollController = ScrollController(); _scrollController = ScrollController();
super.initState(); super.initState();
} }

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:clarity_flutter/clarity_flutter.dart';
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:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
@ -59,6 +60,7 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
@override @override
void initState() { void initState() {
Clarity.setCurrentScreenName('Select Clinic Page');
scheduleMicrotask(() { scheduleMicrotask(() {
bookAppointmentsViewModel.getClinics(); bookAppointmentsViewModel.getClinics();
}); });

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:clarity_flutter/clarity_flutter.dart';
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:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
@ -44,6 +45,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
@override @override
void initState() { void initState() {
Clarity.setCurrentScreenName('Select Doctor Page');
_scrollController = ScrollController(); _scrollController = ScrollController();
scheduleMicrotask(() { scheduleMicrotask(() {
bookAppointmentsViewModel.setIsNearestAppointmentSelected(true); bookAppointmentsViewModel.setIsNearestAppointmentSelected(true);

@ -116,7 +116,7 @@ class _WaitingAppointmentPaymentPageState extends State<WaitingAppointmentPaymen
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h), Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h, fit: BoxFit.contain),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true), LocaleKeys.mada.tr(context: context).toText16(isBold: true),
], ],
@ -135,7 +135,7 @@ class _WaitingAppointmentPaymentPageState extends State<WaitingAppointmentPaymen
), ),
], ],
).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() async {
selectedPaymentMethod = "MADA"; selectedPaymentMethod = "MADA";
if (appState.isPaytabsEnabled) { if (appState.isPaytabsEnabled) {
paytabsViewModel.setPaymentConfiguration( paytabsViewModel.setPaymentConfiguration(
@ -179,7 +179,66 @@ class _WaitingAppointmentPaymentPageState extends State<WaitingAppointmentPaymen
); );
}); });
} else { } else {
openPaymentURL("mada"); if (appState.isPayfortHostedPageEnabled) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPaymentInformation.tr(context: context));
transID = Utils.getAppointmentTransID(
bookAppointmentsViewModel.selectedDoctor.projectID!,
bookAppointmentsViewModel.selectedDoctor.clinicID!,
bookAppointmentsViewModel.selectedDoctor.doctorID!,
);
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(), projectId: bookAppointmentsViewModel.selectedDoctor.projectID!, integrationId: 1);
payfortViewModel.initiatePaymentWithCard(
transactionID: transID,
fileNumber: appState.getAuthenticatedUser()!.patientId.toString(),
amount: myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax!.toString(),
onSucceeded: (paymentResult) async {
print("Payment Success Data: ${paymentResult.toString()}");
LoaderBottomSheet.hideLoader();
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingWaitingAppointment.tr(context: context));
await bookAppointmentsViewModel.insertSpecificAppointmentForWalkIn(onSuccess: (val) async {
String appointmentNo = val.data['AppointmentNo'].toString();
await myAppointmentsViewModel.createAdvancePayment(
paymentMethodName: selectedPaymentMethod,
projectID: bookAppointmentsViewModel.waitingAppointmentProjectID,
clinicID: bookAppointmentsViewModel.selectedDoctor.clinicID!,
appointmentNo: appointmentNo,
payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!,
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
patientID: appState.getAuthenticatedUser()!.patientId.toString(),
patientType: appState.getAuthenticatedUser()!.patientType!,
onSuccess: (value) async {
print(value);
await myAppointmentsViewModel.addAdvanceNumberRequest(
advanceNumber: Utils.isVidaPlusProject(bookAppointmentsViewModel.waitingAppointmentProjectID)
? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString()
: value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(),
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
appointmentNo: appointmentNo,
onSuccess: (value) async {
LoaderBottomSheet.hideLoader();
sendCheckInRequest(bookAppointmentsViewModel.waitingAppointmentNFCCode, appointmentNo, context);
});
});
});
},
onFailed: (err) {
print("Payment Failed Data: ${err.toString()}");
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
} else {
openPaymentURL("mada");
}
} }
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -199,7 +258,7 @@ class _WaitingAppointmentPaymentPageState extends State<WaitingAppointmentPaymen
children: [ children: [
Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h), Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h),
SizedBox(width: 8.h), SizedBox(width: 8.h),
Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h), Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h, fit: BoxFit.contain),
], ],
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -220,7 +279,7 @@ class _WaitingAppointmentPaymentPageState extends State<WaitingAppointmentPaymen
), ),
], ],
).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() async {
selectedPaymentMethod = "VISA"; selectedPaymentMethod = "VISA";
if (appState.isPaytabsEnabled) { if (appState.isPaytabsEnabled) {
paytabsViewModel.setPaymentConfiguration( paytabsViewModel.setPaymentConfiguration(
@ -264,7 +323,66 @@ class _WaitingAppointmentPaymentPageState extends State<WaitingAppointmentPaymen
); );
}); });
} else { } else {
openPaymentURL("visa"); if (appState.isPayfortHostedPageEnabled) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPaymentInformation.tr(context: context));
transID = Utils.getAppointmentTransID(
bookAppointmentsViewModel.selectedDoctor.projectID!,
bookAppointmentsViewModel.selectedDoctor.clinicID!,
bookAppointmentsViewModel.selectedDoctor.doctorID!,
);
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(), projectId: bookAppointmentsViewModel.selectedDoctor.projectID!, integrationId: 1);
payfortViewModel.initiatePaymentWithCard(
transactionID: transID,
fileNumber: appState.getAuthenticatedUser()!.patientId.toString(),
amount: myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax!.toString(),
onSucceeded: (paymentResult) async {
print("Payment Success Data: ${paymentResult.toString()}");
LoaderBottomSheet.hideLoader();
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingWaitingAppointment.tr(context: context));
await bookAppointmentsViewModel.insertSpecificAppointmentForWalkIn(onSuccess: (val) async {
String appointmentNo = val.data['AppointmentNo'].toString();
await myAppointmentsViewModel.createAdvancePayment(
paymentMethodName: selectedPaymentMethod,
projectID: bookAppointmentsViewModel.waitingAppointmentProjectID,
clinicID: bookAppointmentsViewModel.selectedDoctor.clinicID!,
appointmentNo: appointmentNo,
payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!,
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
patientID: appState.getAuthenticatedUser()!.patientId.toString(),
patientType: appState.getAuthenticatedUser()!.patientType!,
onSuccess: (value) async {
print(value);
await myAppointmentsViewModel.addAdvanceNumberRequest(
advanceNumber: Utils.isVidaPlusProject(bookAppointmentsViewModel.waitingAppointmentProjectID)
? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString()
: value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(),
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
appointmentNo: appointmentNo,
onSuccess: (value) async {
LoaderBottomSheet.hideLoader();
sendCheckInRequest(bookAppointmentsViewModel.waitingAppointmentNFCCode, appointmentNo, context);
});
});
});
},
onFailed: (err) {
print("Payment Failed Data: ${err.toString()}");
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
} else {
openPaymentURL("visa");
}
} }
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:clarity_flutter/clarity_flutter.dart';
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';
@ -59,6 +60,7 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
@override @override
void initState() { void initState() {
Clarity.setCurrentScreenName('Select Doctor Free Slot Page');
_calendarController = CalendarController(); _calendarController = CalendarController();
scheduleMicrotask(() { scheduleMicrotask(() {
_events = { _events = {
@ -98,54 +100,56 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
child: Localizations.override( child: Localizations.override(
context: context, context: context,
locale: isArabic ? const Locale('ar', 'SA') : const Locale('en'), locale: isArabic ? const Locale('ar', 'SA') : const Locale('en'),
child: SfCalendar( child: ClarityUnmask(
controller: _calendarController, child: SfCalendar(
minDate: DateTime.now(), controller: _calendarController,
showNavigationArrow: true, minDate: DateTime.now(),
// headerHeight: 60.h, showNavigationArrow: true,
headerStyle: CalendarHeaderStyle( // headerHeight: 60.h,
backgroundColor: AppColors.transparent, headerStyle: CalendarHeaderStyle(
textAlign: isArabic ? TextAlign.end : TextAlign.start, backgroundColor: AppColors.transparent,
textStyle: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.primaryRedColor), textAlign: isArabic ? TextAlign.end : TextAlign.start,
), textStyle: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.primaryRedColor),
viewHeaderStyle: ViewHeaderStyle(
// backgroundColor: AppColors.scaffoldBgColor,
dayTextStyle: TextStyle(fontSize: 14.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.textColor),
),
view: CalendarView.month,
todayHighlightColor: Colors.transparent,
todayTextStyle: TextStyle(color: AppColors.textColor, fontWeight: FontWeight.bold),
selectionDecoration: ShapeDecoration(
color: AppColors.transparent,
shape: SmoothRectangleBorder(
borderRadius: BorderRadius.circular(10.r),
smoothness: 1,
side: BorderSide(color: AppColors.primaryRedColor, width: 1.5),
), ),
), viewHeaderStyle: ViewHeaderStyle(
cellBorderColor: AppColors.transparent, // backgroundColor: AppColors.scaffoldBgColor,
dataSource: MeetingDataSource(_getDataSource()), dayTextStyle: TextStyle(fontSize: 14.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.textColor),
monthCellBuilder: (context, details) => Padding( ),
padding: EdgeInsets.all(12.h), view: CalendarView.month,
child: details.date.day.toString().toText14( todayHighlightColor: Colors.transparent,
isCenter: true, todayTextStyle: TextStyle(color: AppColors.textColor, fontWeight: FontWeight.bold),
color: details.date == _calendarController.selectedDate ? AppColors.primaryRedColor : AppColors.textColor, selectionDecoration: ShapeDecoration(
isEnglishOnly: true, color: AppColors.transparent,
), shape: SmoothRectangleBorder(
), borderRadius: BorderRadius.circular(10.r),
monthViewSettings: MonthViewSettings( smoothness: 1,
dayFormat: "EEE", side: BorderSide(color: AppColors.primaryRedColor, width: 1.5),
appointmentDisplayMode: MonthAppointmentDisplayMode.indicator, ),
showTrailingAndLeadingDates: false,
appointmentDisplayCount: 1,
monthCellStyle: MonthCellStyle(
textStyle: TextStyle(fontSize: 19.f),
), ),
cellBorderColor: AppColors.transparent,
dataSource: MeetingDataSource(_getDataSource()),
monthCellBuilder: (context, details) => Padding(
padding: EdgeInsets.all(12.h),
child: details.date.day.toString().toText14(
isCenter: true,
color: details.date == _calendarController.selectedDate ? AppColors.primaryRedColor : AppColors.textColor,
isEnglishOnly: true,
),
),
monthViewSettings: MonthViewSettings(
dayFormat: "EEE",
appointmentDisplayMode: MonthAppointmentDisplayMode.indicator,
showTrailingAndLeadingDates: false,
appointmentDisplayCount: 1,
monthCellStyle: MonthCellStyle(
textStyle: TextStyle(fontSize: 19.f),
),
),
onTap: (CalendarTapDetails details) {
_calendarController.selectedDate = details.date;
_onDaySelected(details.date!);
},
), ),
onTap: (CalendarTapDetails details) {
_calendarController.selectedDate = details.date;
_onDaySelected(details.date!);
},
), ),
), ),
), ),
@ -153,35 +157,37 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
SizedBox(height: 10.h), SizedBox(height: 10.h),
Transform.translate( Transform.translate(
offset: const Offset(0.0, -10.0), offset: const Offset(0.0, -10.0),
child: selectedDateDisplay.toText16(isBold: true, isEnglishOnly: !isArabic), child: ClarityUnmask(child: selectedDateDisplay.toText16(isBold: true, isEnglishOnly: !isArabic)),
), ),
//TODO: Add Next Day Span here //TODO: Add Next Day Span here
dayEvents.isNotEmpty dayEvents.isNotEmpty
? ConstrainedBox( ? ClarityUnmask(
constraints: BoxConstraints( child: ConstrainedBox(
maxHeight: MediaQuery.of(context).size.height * 0.15, constraints: BoxConstraints(
minHeight: 0, maxHeight: MediaQuery.of(context).size.height * 0.15,
), minHeight: 0,
child: Directionality( ),
textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr, child: Directionality(
child: SingleChildScrollView( textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr,
scrollDirection: Axis.vertical, child: SingleChildScrollView(
child: Wrap( scrollDirection: Axis.vertical,
direction: Axis.horizontal, child: Wrap(
alignment: WrapAlignment.start, direction: Axis.horizontal,
spacing: 6.h, alignment: WrapAlignment.start,
runSpacing: 6.h, spacing: 6.h,
children: List.generate( runSpacing: 6.h,
dayEvents.length, children: List.generate(
(index) => TimeSlotChip( dayEvents.length,
label: dayEvents[index].isoTime!, (index) => TimeSlotChip(
isSelected: index == selectedButtonIndex, label: dayEvents[index].isoTime!,
onTap: () { isSelected: index == selectedButtonIndex,
setState(() { onTap: () {
selectedButtonIndex = index; setState(() {
selectedTime = dayEvents[index].isoTime!; selectedButtonIndex = index;
}); selectedTime = dayEvents[index].isoTime!;
}, });
},
),
), ),
), ),
), ),

@ -59,7 +59,7 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
@override @override
void dispose() { void dispose() {
// Clear errors before disposing // Clear errors before disposing
habibWalletVM.clearAllRechargeErrors(); // habibWalletVM.clearAllRechargeErrors();
_amountFocusNode.dispose(); _amountFocusNode.dispose();
amountTextController.dispose(); amountTextController.dispose();

@ -2,8 +2,12 @@ import 'dart:async';
import 'dart:developer'; import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:amazon_payfort/amazon_payfort.dart';
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:flutter/services.dart';
import 'package:flutter_amazonpaymentservices/flutter_amazonpaymentservices.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.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';
@ -26,6 +30,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/in_app_browser/InAppBrowser.dart'; import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.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:network_info_plus/network_info_plus.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class WalletPaymentConfirmPage extends StatefulWidget { class WalletPaymentConfirmPage extends StatefulWidget {
@ -85,7 +90,7 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h).toShimmer2(isShow: false), Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h, fit: BoxFit.contain).toShimmer2(isShow: false),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: false), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: false),
], ],
@ -104,7 +109,7 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
), ),
], ],
).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() async {
selectedPaymentMethod = "MADA"; selectedPaymentMethod = "MADA";
if (appState.isPaytabsEnabled) { if (appState.isPaytabsEnabled) {
paytabsViewModel.setPaymentConfiguration("Advance Payment", habibWalletVM.walletRechargeAmount); paytabsViewModel.setPaymentConfiguration("Advance Payment", habibWalletVM.walletRechargeAmount);
@ -165,7 +170,80 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
); );
}); });
} else { } else {
openPaymentURL("mada"); if (appState.isPayfortHostedPageEnabled) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPaymentInformation.tr(context: context));
transID = Utils.getAdvancePaymentTransID(habibWalletVM.selectedHospital!.iD!, int.parse(habibWalletVM.fileNumber));
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.advancePayment.getIdFromServiceEnum(), projectId: habibWalletVM.selectedHospital!.iD!, integrationId: 1);
payfortViewModel.initiatePaymentWithCard(
transactionID: transID,
fileNumber: habibWalletVM.fileNumber.toString(),
amount: habibWalletVM.walletRechargeAmount.toString(),
onSucceeded: (paymentResult) async {
print("Payment Success Data: ${paymentResult.toString()}");
LoaderBottomSheet.showLoader();
await habibWalletVM.HISCreateAdvancePayment(
paymentMethodName: selectedPaymentMethod,
paidAmount: habibWalletVM.walletRechargeAmount,
paymentReference: paymentResult.fortId!,
patientID: habibWalletVM.fileNumber,
projectID: habibWalletVM.selectedHospital!.iD!,
depositorName: habibWalletVM.depositorName,
onSuccess: (value) async {
await habibWalletVM.addAdvanceNumberRequest(
advanceNumber: Utils.isVidaPlusProject(habibWalletVM.selectedHospital!.iD)
? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString()
: value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(),
paymentReference: paymentResult.fortId!,
onSuccess: (value) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(getIt.get<NavigationService>().navigatorKey.currentContext!,
child: Utils.getSuccessWidget(loadingText: "Payment Successful!"), callBackFunc: () {
habibWalletVM.setIsWalletAmountToBeLoaded(true);
habibWalletVM.initHabibWalletProvider();
habibWalletVM.getPatientBalanceAmount();
Navigator.of(getIt.get<NavigationService>().navigatorKey.currentContext!).pop();
Navigator.of(getIt.get<NavigationService>().navigatorKey.currentContext!).pop();
}, isFullScreen: false, isCloseButtonVisible: true, isAutoDismiss: true);
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
onFailed: (err) {
print("Payment Failed Data: ${err.toString()}");
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
} else {
openPaymentURL("mada");
}
} }
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -185,7 +263,7 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
children: [ children: [
Utils.buildImgWithAssets(icon: AppAssets.visa, width: 40.h, height: 40.h), Utils.buildImgWithAssets(icon: AppAssets.visa, width: 40.h, height: 40.h),
SizedBox(width: 8.h), SizedBox(width: 8.h),
Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h), Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h, fit: BoxFit.contain),
], ],
).toShimmer2(isShow: false), ).toShimmer2(isShow: false),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -206,7 +284,7 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
), ),
], ],
).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() async {
selectedPaymentMethod = "VISA"; selectedPaymentMethod = "VISA";
if (appState.isPaytabsEnabled) { if (appState.isPaytabsEnabled) {
paytabsViewModel.setPaymentConfiguration("Advance Payment", habibWalletVM.walletRechargeAmount); paytabsViewModel.setPaymentConfiguration("Advance Payment", habibWalletVM.walletRechargeAmount);
@ -267,7 +345,80 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
); );
}); });
} else { } else {
openPaymentURL("visa"); if (appState.isPayfortHostedPageEnabled) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPaymentInformation.tr(context: context));
transID = Utils.getAdvancePaymentTransID(habibWalletVM.selectedHospital!.iD!, int.parse(habibWalletVM.fileNumber));
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.advancePayment.getIdFromServiceEnum(), projectId: habibWalletVM.selectedHospital!.iD!, integrationId: 1);
payfortViewModel.initiatePaymentWithCard(
transactionID: transID,
fileNumber: habibWalletVM.fileNumber.toString(),
amount: habibWalletVM.walletRechargeAmount.toString(),
onSucceeded: (paymentResult) async {
print("Payment Success Data: ${paymentResult.toString()}");
LoaderBottomSheet.showLoader();
await habibWalletVM.HISCreateAdvancePayment(
paymentMethodName: selectedPaymentMethod,
paidAmount: habibWalletVM.walletRechargeAmount,
paymentReference: paymentResult.fortId!,
patientID: habibWalletVM.fileNumber,
projectID: habibWalletVM.selectedHospital!.iD!,
depositorName: habibWalletVM.depositorName,
onSuccess: (value) async {
await habibWalletVM.addAdvanceNumberRequest(
advanceNumber: Utils.isVidaPlusProject(habibWalletVM.selectedHospital!.iD)
? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString()
: value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(),
paymentReference: paymentResult.fortId!,
onSuccess: (value) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(getIt.get<NavigationService>().navigatorKey.currentContext!,
child: Utils.getSuccessWidget(loadingText: "Payment Successful!"), callBackFunc: () {
habibWalletVM.setIsWalletAmountToBeLoaded(true);
habibWalletVM.initHabibWalletProvider();
habibWalletVM.getPatientBalanceAmount();
Navigator.of(getIt.get<NavigationService>().navigatorKey.currentContext!).pop();
Navigator.of(getIt.get<NavigationService>().navigatorKey.currentContext!).pop();
}, isFullScreen: false, isCloseButtonVisible: true, isAutoDismiss: true);
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
onFailed: (err) {
print("Payment Failed Data: ${err.toString()}");
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
} else {
openPaymentURL("visa");
}
} }
}), }),
], ],
@ -626,4 +777,28 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
"", "",
"3"); "3");
} }
Future<void> runSafePayment(Map<String, dynamic> requestParam, {String sdkToken = ""}) async {
dynamic result;
try {
// Force a 100ms microtask buffer to isolate the UI rendering channel
await Future.delayed(const Duration(milliseconds: 350));
result = await FlutterAmazonpaymentservices.normalPay(requestParam, ApiConsts.payFortEnvironmentType, isShowResponsePage: true);
print("Payment Success Data: ${result.toString()}");
} on PlatformException catch (e) {
// If the error is the MethodChannel duplicate reply bug, handle it safely
if (e.message != null && e.message!.contains("Reply already submitted")) {
print("Swallowed native channel race-condition crash safely.");
return;
}
// Process real payment rejection errors here
print("Actual Payment Failure: ${e.details.toString()}");
} catch (error) {
print("Generic Error: $error");
}
}
} }

@ -18,7 +18,7 @@ import 'package:lottie/lottie.dart';
class AppUpdatePage extends StatelessWidget { class AppUpdatePage extends StatelessWidget {
AppUpdatePage({super.key, required this.appUpdateMessage}); AppUpdatePage({super.key, required this.appUpdateMessage});
String appUpdateMessage; final String appUpdateMessage;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -72,21 +72,7 @@ class AppUpdatePage extends StatelessWidget {
openAppUpdateLink() { openAppUpdateLink() {
if (Platform.isAndroid) { if (Platform.isAndroid) {
// _launchURL("https://play.google.com/store/apps/details?id=com.ejada.hmg"); _handleAndroidAppUpdate();
InAppUpdate.checkForUpdate().then((info) {
print("checkForUpdate!!!");
print(info.toString());
if (info.immediateUpdateAllowed) {
print("Immediate Allowed!!!");
InAppUpdate.performImmediateUpdate().then((value) {}).catchError((e) => print(e.toString()));
}
}).catchError((e) {
print(e.toString());
Utils.openWebView(
// url: "https://play.google.com/store/apps/details?id=com.ejada.hmg",
url: "https://play.google.com/store/apps/details?id=com.cloudsolutions.HMGPatientApp",
);
});
} }
if (Platform.isIOS) { if (Platform.isIOS) {
Utils.openWebView( Utils.openWebView(
@ -95,4 +81,43 @@ class AppUpdatePage extends StatelessWidget {
); );
} }
} }
Future<void> _handleAndroidAppUpdate() async {
const fallbackUrl = "https://play.google.com/store/apps/details?id=com.cloudsolutions.HMGPatientApp";
try {
final info = await InAppUpdate.checkForUpdate();
print("InAppUpdate.checkForUpdate -> $info");
if (info != null) {
final immediateAllowed = info.immediateUpdateAllowed ?? false;
final flexibleAllowed = info.flexibleUpdateAllowed ?? false;
print("immediateAllowed: $immediateAllowed, flexibleAllowed: $flexibleAllowed");
if (immediateAllowed) {
print("Performing immediate update...");
await InAppUpdate.performImmediateUpdate();
return;
}
if (flexibleAllowed) {
print("Starting flexible update...");
await InAppUpdate.startFlexibleUpdate();
try {
await InAppUpdate.completeFlexibleUpdate();
} catch (e) {
print("Error completing flexible update: $e");
}
return;
}
}
print("No in-app update allowed/available; opening Play Store page");
Utils.openWebView(url: fallbackUrl);
} catch (e, st) {
print("InAppUpdate.checkForUpdate threw an exception: $e");
print(st);
Utils.openWebView(url: fallbackUrl);
}
}
} }

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:developer'; import 'dart:developer';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:clarity_flutter/clarity_flutter.dart';
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:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
@ -36,11 +37,13 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_queue_page.dart'; import 'package:hmg_patient_app_new/presentation/appointments/appointment_queue_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart'; import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart';
import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/authentication/quick_login.dart'; import 'package:hmg_patient_app_new/presentation/authentication/quick_login.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/contact_us.dart'; import 'package:hmg_patient_app_new/presentation/contact_us/contact_us.dart';
import 'package:hmg_patient_app_new/presentation/hmg_services/services_page.dart'; import 'package:hmg_patient_app_new/presentation/hmg_services/services_page.dart';
import 'package:hmg_patient_app_new/presentation/home/app_update_page.dart';
import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart'; import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/lakum_wallet_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/lakum_wallet_card.dart';
@ -112,6 +115,7 @@ class _LandingPageState extends State<LandingPage> {
@override @override
void initState() { void initState() {
Clarity.setCurrentScreenName('Landing Page');
authVM = context.read<AuthenticationViewModel>(); authVM = context.read<AuthenticationViewModel>();
habibWalletVM = context.read<HabibWalletViewModel>(); habibWalletVM = context.read<HabibWalletViewModel>();
appointmentRatingViewModel = context.read<AppointmentRatingViewModel>(); appointmentRatingViewModel = context.read<AppointmentRatingViewModel>();
@ -288,6 +292,16 @@ class _LandingPageState extends State<LandingPage> {
text: LocaleKeys.loginOrRegister.tr(context: context), text: LocaleKeys.loginOrRegister.tr(context: context),
onPressed: () async { onPressed: () async {
await authVM.onLoginPressed(); await authVM.onLoginPressed();
// Navigator.of(context).push(
// CustomPageRoute(
// // page: NotificationsListPage(),
// page: AppUpdatePage(
// appUpdateMessage:
// "A new update for Dr.Alhabib app is now available with new features and services.\r\nFor technical support:\r\neservices.hmg@drsulaimanalhabib.com"),
// ),
// );
// ...existing commented code... // ...existing commented code...
}, },
backgroundColor: AppColors.secondaryLightRedColor, backgroundColor: AppColors.secondaryLightRedColor,
@ -1393,6 +1407,7 @@ class _LandingPageState extends State<LandingPage> {
orderNo: order.orderNo ?? 0, orderNo: order.orderNo ?? 0,
projectID: order.projectID ?? 0, projectID: order.projectID ?? 0,
projectName: order.projectName ?? "", projectName: order.projectName ?? "",
setupID: order.setupID ?? "",
), ),
), ),
); );

@ -36,6 +36,7 @@ class AncillaryOrderPaymentPage extends StatefulWidget {
final int appointmentNoVida; final int appointmentNoVida;
final int orderNo; final int orderNo;
final int projectID; final int projectID;
final String setupID;
final List<AncillaryOrderProcDetail> selectedProcedures; final List<AncillaryOrderProcDetail> selectedProcedures;
final num totalAmount; final num totalAmount;
@ -45,6 +46,7 @@ class AncillaryOrderPaymentPage extends StatefulWidget {
required this.appointmentNoVida, required this.appointmentNoVida,
required this.orderNo, required this.orderNo,
required this.projectID, required this.projectID,
required this.setupID,
required this.selectedProcedures, required this.selectedProcedures,
required this.totalAmount, required this.totalAmount,
}); });
@ -107,7 +109,7 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Utils.buildImgWithAssets(icon: AppAssets.mada, width: 50.h, height: 50.h), Utils.buildImgWithAssets(icon: AppAssets.mada, width: 80.h, height: 30.h, fit: BoxFit.contain),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: todoVM.isProcessingPayment), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: todoVM.isProcessingPayment),
], ],
@ -126,7 +128,7 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
), ),
], ],
).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() async {
if (!todoVM.isProcessingPayment) { if (!todoVM.isProcessingPayment) {
selectedPaymentMethod = "MADA"; selectedPaymentMethod = "MADA";
if (appState.isPaytabsEnabled) { if (appState.isPaytabsEnabled) {
@ -155,7 +157,48 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
); );
}); });
} else { } else {
_openPaymentURL("mada"); if (appState.isPayfortHostedPageEnabled) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPaymentInformation.tr(context: context));
transID = Utils.getAdvancePaymentTransID(
widget.projectID,
appState.getAuthenticatedUser()!.patientId!,
);
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.ancillaryOrder.getIdFromServiceEnum(),
projectId: widget.projectID,
integrationId: 1,
);
payfortViewModel.initiatePaymentWithCard(
transactionID: transID,
fileNumber: appState.getAuthenticatedUser()!.patientId.toString(),
amount: widget.totalAmount.toString(),
onSucceeded: (paymentResult) async {
print("Payment Success Data: ${paymentResult.toString()}");
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr(context: context));
// Call createAdvancePayment with the payment details
_createAdvancePayment(
paymentAmount: widget.totalAmount,
paymentReference: paymentResult.fortId!,
paymentMethod: selectedPaymentMethod,
);
},
onFailed: (err) {
print("Payment Failed Data: ${err.toString()}");
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
} else {
_openPaymentURL("mada");
}
} }
} }
}), }),
@ -179,7 +222,7 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
children: [ children: [
Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h), Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h),
SizedBox(width: 8.h), SizedBox(width: 8.h),
Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h), Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h, fit: BoxFit.contain),
], ],
).toShimmer2(isShow: todoVM.isProcessingPayment), ).toShimmer2(isShow: todoVM.isProcessingPayment),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -200,7 +243,7 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
), ),
], ],
).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() async {
if (!todoVM.isProcessingPayment) { if (!todoVM.isProcessingPayment) {
selectedPaymentMethod = "VISA"; selectedPaymentMethod = "VISA";
if (appState.isPaytabsEnabled) { if (appState.isPaytabsEnabled) {
@ -229,7 +272,48 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
); );
}); });
} else { } else {
_openPaymentURL("visa"); if (appState.isPayfortHostedPageEnabled) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPaymentInformation.tr(context: context));
transID = Utils.getAdvancePaymentTransID(
widget.projectID,
appState.getAuthenticatedUser()!.patientId!,
);
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.ancillaryOrder.getIdFromServiceEnum(),
projectId: widget.projectID,
integrationId: 1,
);
payfortViewModel.initiatePaymentWithCard(
transactionID: transID,
fileNumber: appState.getAuthenticatedUser()!.patientId.toString(),
amount: widget.totalAmount.toString(),
onSucceeded: (paymentResult) async {
print("Payment Success Data: ${paymentResult.toString()}");
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr(context: context));
// Call createAdvancePayment with the payment details
_createAdvancePayment(
paymentAmount: widget.totalAmount,
paymentReference: paymentResult.fortId!,
paymentMethod: selectedPaymentMethod,
);
},
onFailed: (err) {
print("Payment Failed Data: ${err.toString()}");
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
} else {
_openPaymentURL("visa");
}
} }
} }
}), }),
@ -439,7 +523,7 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
} }
void _createAdvancePayment({ void _createAdvancePayment({
required double paymentAmount, required num paymentAmount,
required String paymentReference, required String paymentReference,
required String paymentMethod, required String paymentMethod,
}) { }) {
@ -455,7 +539,8 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
patientTypeID: user!.patientType ?? 1, patientTypeID: user!.patientType ?? 1,
patientName: "${user.firstName} ${user.lastName}", patientName: "${user.firstName} ${user.lastName}",
patientID: user.patientId!, patientID: user.patientId!,
setupID: "010266", // setupID: "010266",
setupID: widget.setupID,
isAncillaryOrder: true, isAncillaryOrder: true,
onSuccess: (response) { onSuccess: (response) {
// Extract advance number from response // Extract advance number from response

@ -30,6 +30,7 @@ class AncillaryOrderDetailsList extends StatefulWidget {
final int orderNo; final int orderNo;
final int projectID; final int projectID;
final String projectName; final String projectName;
final String setupID;
const AncillaryOrderDetailsList({ const AncillaryOrderDetailsList({
super.key, super.key,
@ -37,6 +38,7 @@ class AncillaryOrderDetailsList extends StatefulWidget {
required this.orderNo, required this.orderNo,
required this.projectID, required this.projectID,
required this.projectName, required this.projectName,
required this.setupID,
}); });
@override @override
@ -683,6 +685,7 @@ class _AncillaryOrderDetailsListState extends State<AncillaryOrderDetailsList> {
selectedProcedures: selectedProcedures, selectedProcedures: selectedProcedures,
totalAmount: _getTotalAmount(), totalAmount: _getTotalAmount(),
appointmentDate: orderData.appointmentDate, appointmentDate: orderData.appointmentDate,
setupID: widget.setupID,
), ),
), ),
); );

@ -84,6 +84,7 @@ class _ToDoPageState extends State<ToDoPage> {
orderNo: order.orderNo ?? 0, orderNo: order.orderNo ?? 0,
projectID: order.projectID ?? 0, projectID: order.projectID ?? 0,
projectName: order.projectName ?? "", projectName: order.projectName ?? "",
setupID: order.setupID ?? "0",
), ),
), ),
); );

@ -60,9 +60,9 @@ class _SplashScreenState extends State<SplashPage> {
ZoomService().initializeZoomSDK(); ZoomService().initializeZoomSDK();
// if (!kDebugMode) { if (!kDebugMode) {
// _initializeClarity(); _initializeClarity();
// } }
if (isAppOpenedFromCall) { if (isAppOpenedFromCall) {
navigateToTeleConsult(); navigateToTeleConsult();
@ -87,7 +87,7 @@ class _SplashScreenState extends State<SplashPage> {
void _initializeClarity() { void _initializeClarity() {
final config = ClarityConfig( final config = ClarityConfig(
projectId: "x0qgorlez4", // You can find it on the Settings page of Clarity dashboard. projectId: "x0qgorlez4", // You can find it on the Settings page of Clarity dashboard.
logLevel: LogLevel.Verbose, // Optional: Set the log level (Verbose, Debug, Info, Warning, Error, None) logLevel: LogLevel.Error, // Optional: Set the log level (Verbose, Debug, Info, Warning, Error, None)
); );
Clarity.initialize(context, config); Clarity.initialize(context, config);

@ -86,6 +86,9 @@ class _AppLanguageChangeState extends State<AppLanguageChange> {
//Reload Emergency Services Data //Reload Emergency Services Data
emergencyServicesViewModel.checkPatientERAdvanceBalance(); emergencyServicesViewModel.checkPatientERAdvanceBalance();
todoSectionViewModel.setIsAncillaryOrdersNeedReloading(true);
todoSectionViewModel.initializeTodoSectionViewModel();
Navigator.pop(context); Navigator.pop(context);
}); });
}), }),

@ -79,7 +79,7 @@ class ImageOptions {
} }
}, },
onFilesTap: () async { onFilesTap: () async {
FilePickerResult? result = await FilePicker.pickFiles( FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom, type: FileType.custom,
allowedExtensions: [ allowedExtensions: [
'jpg', 'jpg',

@ -2,8 +2,8 @@ name: hmg_patient_app_new
description: "New HMG Patient App" description: "New HMG Patient App"
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: 'none' # Remove this line if you wish to publish to pub.dev
#version: 0.0.32+31 version: 0.0.37+36
version: 0.0.5+2 #version: 0.0.6+3
environment: environment:
sdk: ">=3.6.0 <4.0.0" sdk: ">=3.6.0 <4.0.0"
@ -71,8 +71,8 @@ dependencies:
firebase_analytics: ^11.5.1 firebase_analytics: ^11.5.1
firebase_crashlytics: ^4.3.8 firebase_crashlytics: ^4.3.8
jiffy: ^6.4.3 jiffy: ^6.4.3
# hijri_gregorian_calendar: # hijri_gregorian_calendar:
# path: /Users/aamir/StudioProjects/hijri_gregorian_calendar # path: /Users/aamir/StudioProjects/hijri_gregorian_calendar
sms_otp_auto_verify: ^2.2.0 sms_otp_auto_verify: ^2.2.0
web: any web: any
flutter_staggered_animations: ^1.1.1 flutter_staggered_animations: ^1.1.1
@ -89,7 +89,7 @@ dependencies:
path: package/flutter_swiper_view path: package/flutter_swiper_view
flutter_callkit_incoming: ^3.0.0 flutter_callkit_incoming: ^3.0.0
in_app_update: ^4.2.5 in_app_update: ^4.2.5
# device_preview: ^1.3.1 # device_preview: ^1.3.1
image_picker_android: ^0.8.13+16 image_picker_android: ^0.8.13+16
image_picker_platform_interface: ^2.10.0 image_picker_platform_interface: ^2.10.0
@ -106,6 +106,7 @@ dependencies:
flutter_paytabs_bridge: ^2.7.13 flutter_paytabs_bridge: ^2.7.13
clarity_flutter: 1.9.0 clarity_flutter: 1.9.0
flutter_amazonpaymentservices: ^0.0.13
screen_brightness: ^1.0.1 screen_brightness: ^1.0.1
dev_dependencies: dev_dependencies:
@ -179,4 +180,3 @@ flutter:
# SA8345000000815089206001
Loading…
Cancel
Save