diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index b2ea0470..e4e65227 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -86,3 +86,6 @@ @com.peng.bus.PISubscribe ; } -keep class com.peng.pennavmap.models.bus.BackButtonEventBusData { *; } + +-keep interface com.payfort.fortpaymentsdk.** {*;} +-keep class com.payfort.fortpaymentsdk.** {*;} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ba1f830e..70a68707 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -163,7 +163,7 @@ android:enabled="true" android:exported="true" android:hardwareAccelerated="true" - android:launchMode="singleInstance" + android:launchMode="singleTop" android:showOnLockScreen="true" android:theme="@style/LaunchTheme" android:windowSoftInputMode="adjustResize" diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 97aefb12..d39e8bd7 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -971,7 +971,7 @@ "immediateConsultation": "استشارة فورية", "scheduledConsultation": "استشارة مجدولة", "pharmaLiveCare": "لايف كير الصيدلية", - "notSureHelpMeChooseClinic": "هل تريد مساعدة في اختيار العياة؟", + "notSureHelpMeChooseClinic": "هل تريد مساعدة في اختيار العيادة؟", "mentionYourSymptomsAndFindDoctors": "أدخل حالتك الصحية للحصول على قائمة الأطباء", "immediateService": "خدمة فورية", "noNeedToWaitGetMedicalConsultation": "لا حاجة للانتظار، ستحصل على استشارة طبية فورية عبر مكالمة فيديو", @@ -1956,6 +1956,7 @@ "invoiceHistory": "سجل الفواتير", "refund": "استرداد", "invoiceHistory": "سجل الفواتير", - "thisInvoiceIsNotEligibleForRefund": "هذه الفاتورة غير مؤهلة للاسترداد", - "refund": "استرداد" + "refund": "استرداد", + "parkingQR": "رمز الاستجابة السريعة لوقوف السيارات", + "parkingQRDesc": "امسح رمز الاستجابة السريعة (QR Code) أدناه عند بوابة الخروج من موقف السيارات:" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 0d6658c8..61a6da03 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -1939,14 +1939,15 @@ "earned": "Earned", "getParkingQR": "Get Parking QR", "cannotOpenThisFile": "Cannot open this file", - "thisInvoiceIsNotEligibleForRefund": "This invoice is not eligible for refund", "refundDetails": "Refund Details", "requestNo": "Request No.", "referenceNo": "Reference No.", "refund": "Refund", "advanceNumber": "Advance Number", "amountOnly": "Amount", - "invoiceHistory": "Invoice History" + "invoiceHistory": "Invoice History", + "parkingQR": "Parking QR Code", + "parkingQRDesc": "Scan the below QR Code at the exit parking gate:" } diff --git a/ios/Podfile.lock b/ios/Podfile.lock deleted file mode 100644 index 601f8fb0..00000000 --- a/ios/Podfile.lock +++ /dev/null @@ -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 diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index bf6ca95f..0c8a2314 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:developer'; import 'dart:io'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -209,14 +210,14 @@ class ApiClientImp implements ApiClient { body['TokenID'] = "@dm!n"; } - // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 4774167; + body['TokenID'] = "@dm!n"; + // body['PatientID'] = 4774598; // body['PatientID'] = 89754444; // body['PatientTypeID'] = 1; // body['PatientOutSA'] = 0; // body['SessionID'] = "45786230487560q"; - //VIP Patient: 1181868body: + //VIP Patient: 1181868 // body['IdentificationNo'] = "2235558844"; // body['MobileNo'] = "966533147722"; @@ -259,11 +260,16 @@ class ApiClientImp implements ApiClient { body: requestBody, headers: headers, ); - log("response: ${response.body.toString()}"); + // log("response: ${response.body.toString()}"); } on SocketException catch (e) { final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr(); onFailure(message, -1, failureType: ConnectivityFailure(message)); _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; } on http.ClientException catch (e) { 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(); onFailure(message, -1, failureType: ConnectivityFailure(message)); _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; } catch (e) { final message = LocaleKeys.networkUnknownError.tr(); @@ -287,6 +297,11 @@ class ApiClientImp implements ApiClient { if (statusCode < 200 || statusCode >= 400) { onFailure(LocaleKeys.networkErrorWhileFetching.tr(), statusCode, failureType: StatusCodeFailure(LocaleKeys.networkErrorWhileFetching.tr())); 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 { var parsed = json.decode(utf8.decode(response.bodyBytes)); if (isAllowAny) { @@ -388,6 +403,11 @@ class ApiClientImp implements ApiClient { ); 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 { if (parsed['SameClinicApptList'] != null) { @@ -666,4 +686,42 @@ class ApiClientImp implements ApiClient { logApiEndpointError(String endPoint, error, code) { _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 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; + } + } } diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 84f3acd0..aa51a5da 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -1,4 +1,5 @@ import 'package:amazon_payfort/amazon_payfort.dart'; +import 'package:flutter_amazonpaymentservices/environment_type.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; class ApiConsts { @@ -23,6 +24,7 @@ class ApiConsts { static var payFortEnvironment = FortEnvironment.production; 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/PayFortWeb/pages/SendPayFortRequest.aspx'; // Payfort Payment Gateway URL UAT @@ -47,6 +49,7 @@ class ApiConsts { case AppEnvironmentTypeEnum.prod: baseUrl = "https://hmgwebservices.com/"; payFortEnvironment = FortEnvironment.production; + payFortEnvironmentType = EnvironmentType.production; applePayMerchantId = "merchant.com.hmgwebservices"; SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout"; @@ -62,6 +65,7 @@ class ApiConsts { case AppEnvironmentTypeEnum.dev: baseUrl = "https://uat.hmgwebservices.com/"; payFortEnvironment = FortEnvironment.test; + payFortEnvironmentType = EnvironmentType.sandbox; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx'; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; @@ -78,6 +82,7 @@ class ApiConsts { case AppEnvironmentTypeEnum.uat: baseUrl = "https://uat.hmgwebservices.com/"; payFortEnvironment = FortEnvironment.test; + payFortEnvironmentType = EnvironmentType.sandbox; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx'; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; @@ -94,6 +99,7 @@ class ApiConsts { case AppEnvironmentTypeEnum.preProd: baseUrl = "https://webservices.hmg.com/"; payFortEnvironment = FortEnvironment.production; + payFortEnvironmentType = EnvironmentType.production; applePayMerchantId = "merchant.com.hmgwebservices"; SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; @@ -110,6 +116,7 @@ class ApiConsts { case AppEnvironmentTypeEnum.qa: baseUrl = "https://uat.hmgwebservices.com/"; payFortEnvironment = FortEnvironment.test; + payFortEnvironmentType = EnvironmentType.sandbox; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx'; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; @@ -126,6 +133,7 @@ class ApiConsts { case AppEnvironmentTypeEnum.staging: baseUrl = "https://uat.hmgwebservices.com/"; payFortEnvironment = FortEnvironment.test; + payFortEnvironmentType = EnvironmentType.sandbox; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx'; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index aae2d03b..3369ef49 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -48,9 +48,16 @@ class AppState { bool isPaytabsEnabled = false; + bool isPayfortHostedPageEnabled = true; + void setIsPaytabsEnabled(bool value) { - isPaytabsEnabled = value; - // isPaytabsEnabled = false; + // isPaytabsEnabled = value; + isPaytabsEnabled = false; + } + + void setIsPayfortHostedPageEnabled(bool value) { + isPayfortHostedPageEnabled = value; + // isPayfortHostedPageEnabled = false; } int? _superUserID; diff --git a/lib/core/utils/date_util.dart b/lib/core/utils/date_util.dart index eab4c4f1..0994171f 100644 --- a/lib/core/utils/date_util.dart +++ b/lib/core/utils/date_util.dart @@ -9,7 +9,7 @@ class DateUtil { /// convert String To Date function /// [date] String we want to convert 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(); const start = "/Date("; diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 7393a88f..bb6def9e 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -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_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/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/medical_file/medical_file_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.setUserBloodGroup = activation.patientBlodType ?? "N/A"; _appState.setIsPaytabsEnabled(Utils.havePrivilege(120)); + _appState.setIsPayfortHostedPageEnabled(Utils.havePrivilege(121)); // Refresh privileges after user switching to ensure correct privileges are loaded if (isSwitchUser) { @@ -1252,6 +1254,7 @@ class AuthenticationViewModel extends ChangeNotifier { } await clearDefaultInputValues(); myAppointmentsVM.setIsAppointmentDataToBeLoaded(true); + getIt.get().setIsWalletAmountToBeLoaded(true); getIt.get().setIsInsuranceDataToBeLoaded(true); getIt.get().setHasVitalSignDataLoaded(false); if (isUserAgreedBefore) { @@ -1765,6 +1768,7 @@ class AuthenticationViewModel extends ChangeNotifier { }); _appState.setProjectsDetailList(projectDetailListModel); _appState.setIsPaytabsEnabled(Utils.havePrivilege(120)); + _appState.setIsPayfortHostedPageEnabled(Utils.havePrivilege(121)); } }, ); diff --git a/lib/features/authentication/widgets/otp_verification_screen.dart b/lib/features/authentication/widgets/otp_verification_screen.dart index 00eb768f..372f7e8f 100644 --- a/lib/features/authentication/widgets/otp_verification_screen.dart +++ b/lib/features/authentication/widgets/otp_verification_screen.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -139,6 +140,7 @@ class OTPWidgetState extends State with SingleTickerProviderStateMixi @override void initState() { + Clarity.setCurrentScreenName('OTP Verification Screen'); super.initState(); authVm = context.read(); focusNode = widget.focusNode ?? FocusNode(); diff --git a/lib/features/emergency_services/emergency_services_view_model.dart b/lib/features/emergency_services/emergency_services_view_model.dart index d581d0f5..133aa795 100644 --- a/lib/features/emergency_services/emergency_services_view_model.dart +++ b/lib/features/emergency_services/emergency_services_view_model.dart @@ -347,7 +347,7 @@ class EmergencyServicesViewModel extends ChangeNotifier { (failure) { patientHasAdvanceERBalance = false; isERBookAppointment = true; - isAdvanceERBalanceNeedToBeLoaded = true; + isAdvanceERBalanceNeedToBeLoaded = false; if (onSuccess != null) { onSuccess(failure.message); } @@ -357,7 +357,7 @@ class EmergencyServicesViewModel extends ChangeNotifier { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); patientHasAdvanceERBalance = false; isERBookAppointment = true; - isAdvanceERBalanceNeedToBeLoaded = true; + isAdvanceERBalanceNeedToBeLoaded = false; } else if (apiResponse.messageStatus == 1) { patientHasAdvanceERBalance = apiResponse.data; isERBookAppointment = !patientHasAdvanceERBalance; diff --git a/lib/features/habib_wallet/habib_wallet_view_model.dart b/lib/features/habib_wallet/habib_wallet_view_model.dart index 76886045..3bee29de 100644 --- a/lib/features/habib_wallet/habib_wallet_view_model.dart +++ b/lib/features/habib_wallet/habib_wallet_view_model.dart @@ -329,6 +329,7 @@ class HabibWalletViewModel extends ChangeNotifier { isWalletAmountLoading = true; habibWalletAmount = 0; isLakumAccountInfoLoading = true; + yahalaAccountNumber = ""; } isBottomSheetContentLoading = false; walletRechargeAmount = 0; @@ -339,7 +340,6 @@ class HabibWalletViewModel extends ChangeNotifier { fileNumber = ''; depositorName = ''; mobileNumber = ''; - yahalaAccountNumber = ""; notifyListeners(); } @@ -551,7 +551,12 @@ class HabibWalletViewModel extends ChangeNotifier { final result = await habibWalletRepo.getLakumAccountInformation(identificationNumber: getIt.get().getAuthenticatedUser()!.patientIdentificationNo ?? ""); 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 { if (apiResponse.messageStatus == 2) { yahalaAccountNumber = "0"; diff --git a/lib/features/my_appointments/models/resp_models/appointment_parking_QR_response_model.dart b/lib/features/my_appointments/models/resp_models/appointment_parking_QR_response_model.dart index a0a2c4d9..67be0815 100644 --- a/lib/features/my_appointments/models/resp_models/appointment_parking_QR_response_model.dart +++ b/lib/features/my_appointments/models/resp_models/appointment_parking_QR_response_model.dart @@ -54,7 +54,8 @@ class AppointmentParkingQRResponseModel { isUsed = json['IsUsed']; patientID = json['PatientID']; projectID = json['ProjectID']; - ticketBase64 = json['TicketBase64'].cast(); + // ticketBase64 = json['TicketBase64'].cast(); + ticketBase64 = json['TicketBase64String']; ticketID = json['TicketID']; ticketURL = json['TicketURL']; } diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index 5aae8211..90f504d2 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; @@ -103,6 +106,8 @@ class MyAppointmentsViewModel extends ChangeNotifier { AppointmentParkingQRResponseModel? appointmentParkingQRResponseModel; + Uint8List? parkingQRImageBytes; + MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService, required this.appState}); void onTabChange(int index) { @@ -736,6 +741,9 @@ class MyAppointmentsViewModel extends ChangeNotifier { }, (apiResponse) { appointmentParkingQRResponseModel = apiResponse.data!; + parkingQRImageBytes = base64Decode( + appointmentParkingQRResponseModel!.ticketBase64.contains(',') ? appointmentParkingQRResponseModel!.ticketBase64.split(',').last : appointmentParkingQRResponseModel!.ticketBase64); + // parkingQRImageBytes = appointmentParkingQRResponseModel!.ticketBase64; notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); diff --git a/lib/features/payfort/payfort_view_model.dart b/lib/features/payfort/payfort_view_model.dart index dda8f51a..8d26eab1 100644 --- a/lib/features/payfort/payfort_view_model.dart +++ b/lib/features/payfort/payfort_view_model.dart @@ -1,8 +1,14 @@ +import 'dart:convert'; import 'dart:developer'; +import 'dart:io'; import 'package:amazon_payfort/amazon_payfort.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_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/payfort/models/apple_pay_request_insert_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/widgets/loader/bottomsheet_loader.dart'; import 'package:network_info_plus/network_info_plus.dart'; +import 'package:crypto/crypto.dart'; class PayfortViewModel extends ChangeNotifier { PayfortRepo payfortRepo; @@ -27,6 +34,8 @@ class PayfortViewModel extends ChangeNotifier { GetTamaraInstallmentsDetailsResponseModel? getTamaraInstallmentsDetailsResponseModel; bool isTamaraDetailsLoading = false; + SdkTokenResponse? sdkTokenResponse; + PayfortViewModel({required this.payfortRepo, required this.errorHandlerService}); 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 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 _generateSdkResponse({ String? applePayAccessCode, String? merchantIdentifier, @@ -187,6 +290,74 @@ class PayfortViewModel extends ChangeNotifier { return sdkTokenResponse; } + Future 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().getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", + "language": "en", + "customer_email": "CustID_$fileNumber@HMG.com", + "sdk_token": sdkTokenResponse!.sdkToken, + "customer_name": "${getIt.get().getAuthenticatedUser()!.firstName} ${getIt.get().getAuthenticatedUser()!.lastName}", + }; + + LoaderBottomSheet.hideLoader(); + + paymentResult = await FlutterAmazonpaymentservices.normalPay(request, ApiConsts.payFortEnvironmentType, isShowResponsePage: false); + final paymentMap = Map.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 paymentWithApplePay({ SucceededCallback? onSucceeded, FailedCallback? onFailed, @@ -257,7 +428,6 @@ class PayfortViewModel extends ChangeNotifier { onError!(failure.message); }, (apiResponse) { - log(apiResponse.data); if (onSuccess != null) { onSuccess(apiResponse); } diff --git a/lib/features/paytabs/paytabs_view_model.dart b/lib/features/paytabs/paytabs_view_model.dart index 8615ca22..35a924af 100644 --- a/lib/features/paytabs/paytabs_view_model.dart +++ b/lib/features/paytabs/paytabs_view_model.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_paytabs_bridge/BaseBillingShippingInfo.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/PaymentSdkLocale.dart'; import 'package:flutter_paytabs_bridge/PaymentSdkTokenFormat.dart'; @@ -95,6 +97,61 @@ class PayTabsViewModel extends ChangeNotifier { 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 { 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?; + if (event["status"] == "success") { + _callbackFired = true; + // Cast Map to Map + final data = Map.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"); + } + }); + } } diff --git a/lib/features/todo_section/models/resp_models/ancillary_order_list_response_model.dart b/lib/features/todo_section/models/resp_models/ancillary_order_list_response_model.dart index c014bee7..95f0baaa 100644 --- a/lib/features/todo_section/models/resp_models/ancillary_order_list_response_model.dart +++ b/lib/features/todo_section/models/resp_models/ancillary_order_list_response_model.dart @@ -78,6 +78,7 @@ class AncillaryOrderItem { int? orderNo; String? projectName; // Added from parent AncillaryOrderGroup int? projectID; // Added from parent AncillaryOrderGroup + String? setupID; // Added from parent AncillaryOrderGroup AncillaryOrderItem({ this.ancillaryProcedureListModels, @@ -94,9 +95,10 @@ class AncillaryOrderItem { this.orderNo, this.projectName, this.projectID, + this.setupID, }); - factory AncillaryOrderItem.fromJson(Map json, {String? projectName, int? projectID}) => AncillaryOrderItem( + factory AncillaryOrderItem.fromJson(Map json, {String? projectName, int? projectID, String? setupID}) => AncillaryOrderItem( ancillaryProcedureListModels: json['AncillaryProcedureListModels'], appointmentDate: DateUtil.convertStringToDate(json['AppointmentDate']), appointmentNo: json['AppointmentNo'] as int?, @@ -111,5 +113,6 @@ class AncillaryOrderItem { orderNo: json['OrderNo'] as int?, projectName: projectName, projectID: projectID, + setupID: setupID, ); } diff --git a/lib/features/todo_section/todo_section_repo.dart b/lib/features/todo_section/todo_section_repo.dart index 6d58d801..acaa8c4c 100644 --- a/lib/features/todo_section/todo_section_repo.dart +++ b/lib/features/todo_section/todo_section_repo.dart @@ -20,7 +20,7 @@ abstract class TodoSectionRepo { Future> createAdvancePayment({ required int projectID, - required double paymentAmount, + required num paymentAmount, required String paymentReference, required String paymentMethodName, required int patientTypeID, @@ -85,11 +85,13 @@ class TodoSectionRepoImp implements TodoSectionRepo { final ordersList = group['AncillaryOrderList'] as List; final projectName = group['ProjectName'] as String?; final projectID = group['ProjectID'] as int?; + final setupID = group['SetupID'] as String?; + // Parse each order item in the group for (var orderJson in ordersList) { if (orderJson is Map) { - 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 Future> createAdvancePayment({ required int projectID, - required double paymentAmount, + required num paymentAmount, required String paymentReference, required String paymentMethodName, required int patientTypeID, diff --git a/lib/features/todo_section/todo_section_view_model.dart b/lib/features/todo_section/todo_section_view_model.dart index 7d1fb88a..2f7cba35 100644 --- a/lib/features/todo_section/todo_section_view_model.dart +++ b/lib/features/todo_section/todo_section_view_model.dart @@ -158,7 +158,7 @@ class TodoSectionViewModel extends ChangeNotifier { Future createAdvancePayment({ required int projectID, - required double paymentAmount, + required num paymentAmount, required String paymentReference, required String paymentMethodName, required int patientTypeID, diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index e756459d..8d29d818 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1907,10 +1907,6 @@ abstract class LocaleKeys { static const noProceduresAvailableForRefund = 'noProceduresAvailableForRefund'; static const selectAll = 'selectAll'; 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 bankTransfer = 'bankTransfer'; static const selectRefundReason = 'selectRefundReason'; @@ -1923,6 +1919,10 @@ abstract class LocaleKeys { static const refundReason6 = 'refundReason6'; static const refundReason7 = 'refundReason7'; 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 points = 'points'; static const transactions = 'transactions'; @@ -1938,5 +1938,7 @@ abstract class LocaleKeys { static const amountOnly = 'amountOnly'; static const invoiceHistory = 'invoiceHistory'; static const refund = 'refund'; + static const parkingQR = 'parkingQR'; + static const parkingQRDesc = 'parkingQRDesc'; } diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 46fd5209..daf0839a 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui' as ui; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; @@ -85,6 +86,7 @@ class _AppointmentDetailsPageState extends State { @override void initState() { + Clarity.setCurrentScreenName('Appointment Details Page'); super.initState(); scheduleMicrotask(() async { @@ -284,7 +286,7 @@ class _AppointmentDetailsPageState extends State { AppointmentDoctorCard( // renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) || renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) || - !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)), + !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID ?? 0)), patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, onAskDoctorTap: () async { LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingDoctorAvailability.tr(context: context)); @@ -575,7 +577,7 @@ class _AppointmentDetailsPageState extends State { iconColor: AppColors.transparent, fit: BoxFit.cover, width: double.infinity, - ), + height: 180.h), ), ), Positioned( @@ -875,35 +877,73 @@ class _AppointmentDetailsPageState extends State { } }) : SizedBox.shrink(), - // (widget.patientAppointmentHistoryResponseModel.isParkingAvailable ?? false) - // ? MedicalFileCard( - // label: LocaleKeys.getParkingQR.tr(context: context), - // textColor: AppColors.blackColor, - // backgroundColor: AppColors.whiteColor, - // svgIcon: AppAssets.appointment_parking_icon, - // isLargeText: true, - // iconSize: 36.w, - // ).onPress(() { - // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context)); - // myAppointmentsViewModel.getParkingQR( - // widget.patientAppointmentHistoryResponseModel.appointmentNo, - // widget.patientAppointmentHistoryResponseModel.projectID, - // onSuccess: (val) { - // LoaderBottomSheet.hideLoader(); - // }, - // onError: (err) { - // LoaderBottomSheet.hideLoader(); - // showCommonBottomSheetWithoutHeight( - // context, - // child: Utils.getErrorWidget(loadingText: err), - // callBackFunc: () {}, - // isFullScreen: false, - // isCloseButtonVisible: true, - // ); - // }, - // ); - // }) - // : SizedBox.shrink(), + (widget.patientAppointmentHistoryResponseModel.isParkingAvailable ?? false) + ? MedicalFileCard( + label: LocaleKeys.getParkingQR.tr(context: context), + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.appointment_parking_icon, + isLargeText: true, + iconSize: 36.w, + ).onPress(() { + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context)); + myAppointmentsViewModel.getParkingQR( + widget.patientAppointmentHistoryResponseModel.appointmentNo, + widget.patientAppointmentHistoryResponseModel.projectID, + onSuccess: (val) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.parkingQR.tr(context: context), + context, + child: Column( + children: [ + LocaleKeys.parkingQRDesc.tr(context: context).toText16(), + SizedBox(height: 16.h), + Image.memory( + myAppointmentsViewModel.parkingQRImageBytes!, + width: 156.w, + height: 156.h, + fit: BoxFit.cover, + gaplessPlayback: true, // Prevents blink during image rebuild + ), + 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 { case 50: // return LocaleKeys.confirmLiveCare.tr(context: context); case 90: + Clarity.setCurrentScreenName('Appointment CheckIn Page'); showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.onlineCheckIn.tr(context: context), child: isAppointmentWithin4Hours @@ -1281,9 +1322,9 @@ class _AppointmentDetailsPageState extends State { ) : Utils.getOnlineCheckInWidget( loadingText: LocaleKeys.onlineCheckIn24HoursOnly.tr(context: context), - ), - callBackFunc: () {}, - isFullScreen: false); + ), callBackFunc: () { + Clarity.setCurrentScreenName('Appointment Details Page'); + }, isFullScreen: false); default: // return "No Action".needTranslation; } diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index f3aff049..79b99a1e 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.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/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/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/payfort/models/apple_pay_request_insert_model.dart'; @@ -59,6 +61,7 @@ class _AppointmentPaymentPageState extends State { @override void initState() { scheduleMicrotask(() { + Clarity.setCurrentScreenName('Appointment Payment Page'); payfortViewModel.initPayfortViewModel(); payfortViewModel.setIsApplePayConfigurationLoading(false); myAppointmentsViewModel.getPatientShareAppointment( @@ -112,8 +115,7 @@ class _AppointmentPaymentPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h) - .toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), + Utils.buildImgWithAssets(icon: AppAssets.mada, width: 80.h, height: 30.h, fit: BoxFit.contain).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), SizedBox(height: 16.h), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ], @@ -132,7 +134,7 @@ class _AppointmentPaymentPageState extends State { ), ], ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h).onPress(() { + ).paddingSymmetrical(24.h, 0.h).onPress(() async { selectedPaymentMethod = "MADA"; if (appState.isPaytabsEnabled) { paytabsViewModel.setPaymentConfiguration( @@ -212,7 +214,100 @@ class _AppointmentPaymentPageState extends State { ); }); } 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), @@ -232,7 +327,7 @@ class _AppointmentPaymentPageState extends State { children: [ Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.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), SizedBox(height: 16.h), @@ -255,7 +350,7 @@ class _AppointmentPaymentPageState extends State { ), ], ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h).onPress(() { + ).paddingSymmetrical(24.h, 0.h).onPress(() async { selectedPaymentMethod = "VISA"; if (appState.isPaytabsEnabled) { paytabsViewModel.setPaymentConfiguration( @@ -335,7 +430,110 @@ class _AppointmentPaymentPageState extends State { ); }); } 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().setIsWalletAmountToBeLoaded(true); + getIt.get().initHabibWalletProvider(); + getIt.get().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().setIsWalletAmountToBeLoaded(true); + getIt.get().initHabibWalletProvider(); + getIt.get().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), @@ -376,7 +574,108 @@ class _AppointmentPaymentPageState extends State { ).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(24.h, 0.h).onPress(() { 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().setIsWalletAmountToBeLoaded(true); + getIt.get().initHabibWalletProvider(); + getIt.get().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().setIsWalletAmountToBeLoaded(true); + getIt.get().initHabibWalletProvider(); + getIt.get().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(), ], @@ -443,21 +742,24 @@ class _AppointmentPaymentPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ LocaleKeys.amountBeforeTax.tr(context: context).toText14(isBold: true), - Utils.getPaymentAmountWithSymbol((myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare ?? 0).toString().toText16(isBold: true), AppColors.blackColor, - 13, - isSaudiCurrency: true), + ClarityUnmask( + child: Utils.getPaymentAmountWithSymbol( + (myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare ?? 0).toString().toText16(isBold: true), AppColors.blackColor, 13, + isSaudiCurrency: true), + ), ], ).paddingSymmetrical(24.h, 0.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "VAT 15%".toText14(isBold: true, color: AppColors.greyTextColor), - Utils.getPaymentAmountWithSymbol( - (myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount ?? 0).toString() - .toText14(isBold: true, color: AppColors.greyTextColor), - AppColors.greyTextColor, - 13, - isSaudiCurrency: true), + ClarityUnmask( + child: Utils.getPaymentAmountWithSymbol( + (myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount ?? 0).toString().toText14(isBold: true, color: AppColors.greyTextColor), + AppColors.greyTextColor, + 13, + isSaudiCurrency: true), + ), ], ).paddingSymmetrical(24.h, 0.h), SizedBox(height: 17.h), @@ -465,10 +767,11 @@ class _AppointmentPaymentPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "".toText14(isBold: true), - Utils.getPaymentAmountWithSymbol( - (myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax ?? 0).toString().toText24(isBold: true), AppColors.blackColor, - 17, - isSaudiCurrency: true), + ClarityUnmask( + child: Utils.getPaymentAmountWithSymbol( + (myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax ?? 0).toString().toText24(isBold: true), AppColors.blackColor, 17, + isSaudiCurrency: true), + ), ], ).paddingSymmetrical(24.h, 0.h), Platform.isIOS @@ -566,6 +869,11 @@ class _AppointmentPaymentPageState extends State { myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.getPatientAppointments(true, false); + + getIt.get().setIsWalletAmountToBeLoaded(true); + getIt.get().initHabibWalletProvider(); + getIt.get().getPatientBalanceAmount(); + LoaderBottomSheet.hideLoader(); Navigator.pushAndRemoveUntil( context, @@ -587,6 +895,11 @@ class _AppointmentPaymentPageState extends State { myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.getPatientAppointments(true, false); + + getIt.get().setIsWalletAmountToBeLoaded(true); + getIt.get().initHabibWalletProvider(); + getIt.get().getPatientBalanceAmount(); + LoaderBottomSheet.hideLoader(); Navigator.pushAndRemoveUntil( context, @@ -654,6 +967,11 @@ class _AppointmentPaymentPageState extends State { myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.getPatientAppointments(true, false); + + getIt.get().setIsWalletAmountToBeLoaded(true); + getIt.get().initHabibWalletProvider(); + getIt.get().getPatientBalanceAmount(); + LoaderBottomSheet.hideLoader(); Navigator.pushAndRemoveUntil( context, @@ -675,6 +993,11 @@ class _AppointmentPaymentPageState extends State { myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.getPatientAppointments(true, false); + + getIt.get().setIsWalletAmountToBeLoaded(true); + getIt.get().initHabibWalletProvider(); + getIt.get().getPatientBalanceAmount(); + LoaderBottomSheet.hideLoader(); Navigator.pushAndRemoveUntil( context, @@ -831,6 +1154,11 @@ class _AppointmentPaymentPageState extends State { myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.getPatientAppointments(true, false); + + getIt.get().setIsWalletAmountToBeLoaded(true); + getIt.get().initHabibWalletProvider(); + getIt.get().getPatientBalanceAmount(); + LoaderBottomSheet.hideLoader(); Navigator.pushAndRemoveUntil( context, @@ -852,6 +1180,11 @@ class _AppointmentPaymentPageState extends State { myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.getPatientAppointments(true, false); + + getIt.get().setIsWalletAmountToBeLoaded(true); + getIt.get().initHabibWalletProvider(); + getIt.get().getPatientBalanceAmount(); + LoaderBottomSheet.hideLoader(); Navigator.pushAndRemoveUntil( context, diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 8e871dcb..39251cd6 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:ui' as ui; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -311,16 +312,18 @@ class _AppointmentCardState extends State { ? '${(widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital").substring(0, 15)}...' : widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital") .toShimmer2(isShow: widget.isLoading), - Directionality( - textDirection: ui.TextDirection.ltr, - child: AppCustomChipWidget( - labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), - icon: AppAssets.appointment_calendar_icon, - richText: widget.isLoading - ? 'Cardiology'.toText10().toShimmer2(isShow: widget.isLoading) - : "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)}" - .toText10(isEnglishOnly: true, isBold: true), - ).toShimmer2(isShow: widget.isLoading), + ClarityUnmask( + child: Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), + icon: AppAssets.appointment_calendar_icon, + richText: widget.isLoading + ? 'Cardiology'.toText10().toShimmer2(isShow: widget.isLoading) + : "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)}" + .toText10(isEnglishOnly: true, isBold: true), + ).toShimmer2(isShow: widget.isLoading), + ), ), // AppCustomChipWidget( diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index d064b4e8..59783f20 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -1,5 +1,6 @@ import 'dart:ui' as ui; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -135,17 +136,18 @@ class AppointmentDoctorCard extends StatelessWidget { labelText: patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital", labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), ), - Directionality( - textDirection: ui.TextDirection.ltr, - child: AppCustomChipWidget( - labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w), - icon: AppAssets.doctor_calendar_icon, - richText: - "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang( - DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), - false, - )}" - .toText10(isBold: true), + ClarityUnmask( + child: Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w), + icon: AppAssets.doctor_calendar_icon, + richText: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang( + DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), + false, + )}" + .toText10(isBold: true), + ), ), ), AppCustomChipWidget( diff --git a/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart b/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart index 1343c36f..141ddf52 100644 --- a/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart +++ b/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart @@ -98,7 +98,7 @@ class DoctorResponseTransactions extends StatelessWidget { color: AppColors.infoColor.withAlpha(20), borderRadius: BorderRadius.circular(8.r), ), - child: doctorResponseModel.requestTypeDescription!.toText12(color: AppColors.infoColor, isBold: true), + child: (doctorResponseModel.requestTypeDescription ?? "").toText12(color: AppColors.infoColor, isBold: true), ), ], ), diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart index 21f46bea..c38e9ad9 100644 --- a/lib/presentation/authentication/login.dart +++ b/lib/presentation/authentication/login.dart @@ -1,3 +1,4 @@ +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -32,6 +33,7 @@ class LoginScreenState extends State { @override void initState() { + Clarity.setCurrentScreenName('Login Screen'); super.initState(); _nationalIdFocusNode = FocusNode(); @@ -97,7 +99,7 @@ class LoginScreenState extends State { hintText: "xxxxxxxxx", controller: authVm.nationalIdController, focusNode: _nationalIdFocusNode, - keyboardType: TextInputType.text, + keyboardType: TextInputType.number, isEnable: true, prefix: null, autoFocus: true, diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart index 7f0cdc34..19080ae8 100644 --- a/lib/presentation/authentication/saved_login_screen.dart +++ b/lib/presentation/authentication/saved_login_screen.dart @@ -1,5 +1,6 @@ import 'dart:ui' as ui; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -37,6 +38,7 @@ class _SavedLogin extends State { @override void initState() { + Clarity.setCurrentScreenName('Last Login Screen'); authVm = context.read(); appState = getIt.get(); loginType = LoginTypeExtension.fromValue(appState.getSelectDeviceByImeiRespModelElement!.logInType!)!; @@ -58,7 +60,6 @@ class _SavedLogin extends State { if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { authVm.loginWithFingerPrintFace(() {}); } - super.initState(); } diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index 6bbac9ec..d542bb7f 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -57,6 +58,7 @@ class _BookAppointmentPageState extends State { @override void initState() { + Clarity.setCurrentScreenName('Book Appointment Page'); authVM = context.read(); scheduleMicrotask(() { // bookAppointmentsViewModel.selectedTabIndex = 0; diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart index ed323629..8bbf23af 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart @@ -86,8 +86,8 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { AppCustomChipWidget( richText: Row( children: [ - "${appState.getAuthenticatedUser()!.age} ".toText10(color: AppColors.blackColor, isEnglishOnly: true), - LocaleKeys.yearsOld.tr(context: context).toText10(color: AppColors.blackColor), + "${appState.getAuthenticatedUser()!.age} ".toText10(color: AppColors.blackColor, isBold: true), + LocaleKeys.yearsOld.tr(context: context).toText10(color: AppColors.blackColor, isBold: true), ], ), ), diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart index b0f00317..aa23fef8 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart @@ -106,7 +106,7 @@ class _ImmediateLiveCarePaymentPageState extends State 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), @@ -189,7 +248,7 @@ class _ImmediateLiveCarePaymentPageState extends State 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), @@ -584,7 +702,7 @@ class _ImmediateLiveCarePaymentPageState extends State { Uint8List? _cachedImageBytes; String? _cachedImageDataHash; + @override + void initState() { + Clarity.setCurrentScreenName('Appointment Review Page'); + super.initState(); + } + @override Widget build(BuildContext context) { bookAppointmentsViewModel = Provider.of(context, listen: false); diff --git a/lib/presentation/book_appointment/search_doctor_by_name.dart b/lib/presentation/book_appointment/search_doctor_by_name.dart index 3222fbd8..438cd746 100644 --- a/lib/presentation/book_appointment/search_doctor_by_name.dart +++ b/lib/presentation/book_appointment/search_doctor_by_name.dart @@ -1,3 +1,4 @@ +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -39,6 +40,7 @@ class _SearchDoctorByNameState extends State { @override void initState() { + Clarity.setCurrentScreenName('Search By Doctor Name Page'); _scrollController = ScrollController(); super.initState(); } diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index 05abdb8a..cf1cd897 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -59,6 +60,7 @@ class _SelectClinicPageState extends State { @override void initState() { + Clarity.setCurrentScreenName('Select Clinic Page'); scheduleMicrotask(() { bookAppointmentsViewModel.getClinics(); }); diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart index d9012573..26ab5519 100644 --- a/lib/presentation/book_appointment/select_doctor_page.dart +++ b/lib/presentation/book_appointment/select_doctor_page.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -44,6 +45,7 @@ class _SelectDoctorPageState extends State { @override void initState() { + Clarity.setCurrentScreenName('Select Doctor Page'); _scrollController = ScrollController(); scheduleMicrotask(() { bookAppointmentsViewModel.setIsNearestAppointmentSelected(true); diff --git a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart index c40c712c..c7ef62bb 100644 --- a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart +++ b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart @@ -116,7 +116,7 @@ class _WaitingAppointmentPaymentPageState extends State { @override void initState() { + Clarity.setCurrentScreenName('Select Doctor Free Slot Page'); _calendarController = CalendarController(); scheduleMicrotask(() { _events = { @@ -98,54 +100,56 @@ class _AppointmentCalendarState extends State { child: Localizations.override( context: context, locale: isArabic ? const Locale('ar', 'SA') : const Locale('en'), - child: SfCalendar( - controller: _calendarController, - minDate: DateTime.now(), - showNavigationArrow: true, - // headerHeight: 60.h, - headerStyle: CalendarHeaderStyle( - backgroundColor: AppColors.transparent, - 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), + child: ClarityUnmask( + child: SfCalendar( + controller: _calendarController, + minDate: DateTime.now(), + showNavigationArrow: true, + // headerHeight: 60.h, + headerStyle: CalendarHeaderStyle( + backgroundColor: AppColors.transparent, + textAlign: isArabic ? TextAlign.end : TextAlign.start, + textStyle: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.primaryRedColor), ), - ), - 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), + 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), + ), ), + 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 { SizedBox(height: 10.h), Transform.translate( 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 dayEvents.isNotEmpty - ? ConstrainedBox( - constraints: BoxConstraints( - maxHeight: MediaQuery.of(context).size.height * 0.15, - minHeight: 0, - ), - child: Directionality( - textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Wrap( - direction: Axis.horizontal, - alignment: WrapAlignment.start, - spacing: 6.h, - runSpacing: 6.h, - children: List.generate( - dayEvents.length, - (index) => TimeSlotChip( - label: dayEvents[index].isoTime!, - isSelected: index == selectedButtonIndex, - onTap: () { - setState(() { - selectedButtonIndex = index; - selectedTime = dayEvents[index].isoTime!; - }); - }, + ? ClarityUnmask( + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.15, + minHeight: 0, + ), + child: Directionality( + textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.start, + spacing: 6.h, + runSpacing: 6.h, + children: List.generate( + dayEvents.length, + (index) => TimeSlotChip( + label: dayEvents[index].isoTime!, + isSelected: index == selectedButtonIndex, + onTap: () { + setState(() { + selectedButtonIndex = index; + selectedTime = dayEvents[index].isoTime!; + }); + }, + ), ), ), ), diff --git a/lib/presentation/habib_wallet/recharge_wallet_page.dart b/lib/presentation/habib_wallet/recharge_wallet_page.dart index 2810b221..ded7aaeb 100644 --- a/lib/presentation/habib_wallet/recharge_wallet_page.dart +++ b/lib/presentation/habib_wallet/recharge_wallet_page.dart @@ -59,7 +59,7 @@ class _RechargeWalletPageState extends State { @override void dispose() { // Clear errors before disposing - habibWalletVM.clearAllRechargeErrors(); + // habibWalletVM.clearAllRechargeErrors(); _amountFocusNode.dispose(); amountTextController.dispose(); diff --git a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart index e52e3548..f07cbf63 100644 --- a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart +++ b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart @@ -2,8 +2,12 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; +import 'package:amazon_payfort/amazon_payfort.dart'; import 'package:easy_localization/easy_localization.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_state.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/in_app_browser/InAppBrowser.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'; class WalletPaymentConfirmPage extends StatefulWidget { @@ -85,7 +90,7 @@ class _WalletPaymentConfirmPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, 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), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: false), ], @@ -104,7 +109,7 @@ class _WalletPaymentConfirmPageState extends State { ), ], ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h).onPress(() { + ).paddingSymmetrical(24.h, 0.h).onPress(() async { selectedPaymentMethod = "MADA"; if (appState.isPaytabsEnabled) { paytabsViewModel.setPaymentConfiguration("Advance Payment", habibWalletVM.walletRechargeAmount); @@ -165,7 +170,80 @@ class _WalletPaymentConfirmPageState extends State { ); }); } 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().navigatorKey.currentContext!, + child: Utils.getSuccessWidget(loadingText: "Payment Successful!"), callBackFunc: () { + habibWalletVM.setIsWalletAmountToBeLoaded(true); + habibWalletVM.initHabibWalletProvider(); + habibWalletVM.getPatientBalanceAmount(); + Navigator.of(getIt.get().navigatorKey.currentContext!).pop(); + Navigator.of(getIt.get().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), @@ -185,7 +263,7 @@ class _WalletPaymentConfirmPageState extends State { children: [ Utils.buildImgWithAssets(icon: AppAssets.visa, width: 40.h, height: 40.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), SizedBox(height: 16.h), @@ -206,7 +284,7 @@ class _WalletPaymentConfirmPageState extends State { ), ], ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h).onPress(() { + ).paddingSymmetrical(24.h, 0.h).onPress(() async { selectedPaymentMethod = "VISA"; if (appState.isPaytabsEnabled) { paytabsViewModel.setPaymentConfiguration("Advance Payment", habibWalletVM.walletRechargeAmount); @@ -267,7 +345,80 @@ class _WalletPaymentConfirmPageState extends State { ); }); } 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().navigatorKey.currentContext!, + child: Utils.getSuccessWidget(loadingText: "Payment Successful!"), callBackFunc: () { + habibWalletVM.setIsWalletAmountToBeLoaded(true); + habibWalletVM.initHabibWalletProvider(); + habibWalletVM.getPatientBalanceAmount(); + Navigator.of(getIt.get().navigatorKey.currentContext!).pop(); + Navigator.of(getIt.get().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 { "", "3"); } + + Future runSafePayment(Map 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"); + } + } } diff --git a/lib/presentation/home/app_update_page.dart b/lib/presentation/home/app_update_page.dart index 8750fcb7..37dab487 100644 --- a/lib/presentation/home/app_update_page.dart +++ b/lib/presentation/home/app_update_page.dart @@ -18,7 +18,7 @@ import 'package:lottie/lottie.dart'; class AppUpdatePage extends StatelessWidget { AppUpdatePage({super.key, required this.appUpdateMessage}); - String appUpdateMessage; + final String appUpdateMessage; @override Widget build(BuildContext context) { @@ -72,21 +72,7 @@ class AppUpdatePage extends StatelessWidget { openAppUpdateLink() { if (Platform.isAndroid) { - // _launchURL("https://play.google.com/store/apps/details?id=com.ejada.hmg"); - 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", - ); - }); + _handleAndroidAppUpdate(); } if (Platform.isIOS) { Utils.openWebView( @@ -95,4 +81,43 @@ class AppUpdatePage extends StatelessWidget { ); } } + + Future _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); + } + } } diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 40028315..5f46f94f 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:developer'; import 'dart:ui' as ui; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.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/my_appointments_page.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/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/contact_us/contact_us.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/widgets/habib_wallet_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/lakum_wallet_card.dart'; @@ -112,6 +115,7 @@ class _LandingPageState extends State { @override void initState() { + Clarity.setCurrentScreenName('Landing Page'); authVM = context.read(); habibWalletVM = context.read(); appointmentRatingViewModel = context.read(); @@ -288,6 +292,16 @@ class _LandingPageState extends State { text: LocaleKeys.loginOrRegister.tr(context: context), onPressed: () async { 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... }, backgroundColor: AppColors.secondaryLightRedColor, @@ -1393,6 +1407,7 @@ class _LandingPageState extends State { orderNo: order.orderNo ?? 0, projectID: order.projectID ?? 0, projectName: order.projectName ?? "", + setupID: order.setupID ?? "", ), ), ); diff --git a/lib/presentation/todo_section/ancillary_order_payment_page.dart b/lib/presentation/todo_section/ancillary_order_payment_page.dart index 8cdd0c82..99ed0928 100644 --- a/lib/presentation/todo_section/ancillary_order_payment_page.dart +++ b/lib/presentation/todo_section/ancillary_order_payment_page.dart @@ -36,6 +36,7 @@ class AncillaryOrderPaymentPage extends StatefulWidget { final int appointmentNoVida; final int orderNo; final int projectID; + final String setupID; final List selectedProcedures; final num totalAmount; @@ -45,6 +46,7 @@ class AncillaryOrderPaymentPage extends StatefulWidget { required this.appointmentNoVida, required this.orderNo, required this.projectID, + required this.setupID, required this.selectedProcedures, required this.totalAmount, }); @@ -107,7 +109,7 @@ class _AncillaryOrderPaymentPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, 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), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: todoVM.isProcessingPayment), ], @@ -126,7 +128,7 @@ class _AncillaryOrderPaymentPageState extends State { ), ], ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h).onPress(() { + ).paddingSymmetrical(24.h, 0.h).onPress(() async { if (!todoVM.isProcessingPayment) { selectedPaymentMethod = "MADA"; if (appState.isPaytabsEnabled) { @@ -155,7 +157,48 @@ class _AncillaryOrderPaymentPageState extends State { ); }); } 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 { children: [ Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.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), SizedBox(height: 16.h), @@ -200,7 +243,7 @@ class _AncillaryOrderPaymentPageState extends State { ), ], ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h).onPress(() { + ).paddingSymmetrical(24.h, 0.h).onPress(() async { if (!todoVM.isProcessingPayment) { selectedPaymentMethod = "VISA"; if (appState.isPaytabsEnabled) { @@ -229,7 +272,48 @@ class _AncillaryOrderPaymentPageState extends State { ); }); } 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 { } void _createAdvancePayment({ - required double paymentAmount, + required num paymentAmount, required String paymentReference, required String paymentMethod, }) { @@ -455,7 +539,8 @@ class _AncillaryOrderPaymentPageState extends State { patientTypeID: user!.patientType ?? 1, patientName: "${user.firstName} ${user.lastName}", patientID: user.patientId!, - setupID: "010266", + // setupID: "010266", + setupID: widget.setupID, isAncillaryOrder: true, onSuccess: (response) { // Extract advance number from response diff --git a/lib/presentation/todo_section/ancillary_procedures_details_page.dart b/lib/presentation/todo_section/ancillary_procedures_details_page.dart index fdcdad9a..c34031f4 100644 --- a/lib/presentation/todo_section/ancillary_procedures_details_page.dart +++ b/lib/presentation/todo_section/ancillary_procedures_details_page.dart @@ -30,6 +30,7 @@ class AncillaryOrderDetailsList extends StatefulWidget { final int orderNo; final int projectID; final String projectName; + final String setupID; const AncillaryOrderDetailsList({ super.key, @@ -37,6 +38,7 @@ class AncillaryOrderDetailsList extends StatefulWidget { required this.orderNo, required this.projectID, required this.projectName, + required this.setupID, }); @override @@ -683,6 +685,7 @@ class _AncillaryOrderDetailsListState extends State { selectedProcedures: selectedProcedures, totalAmount: _getTotalAmount(), appointmentDate: orderData.appointmentDate, + setupID: widget.setupID, ), ), ); diff --git a/lib/presentation/todo_section/todo_page.dart b/lib/presentation/todo_section/todo_page.dart index 161ffaf9..d669668a 100644 --- a/lib/presentation/todo_section/todo_page.dart +++ b/lib/presentation/todo_section/todo_page.dart @@ -84,6 +84,7 @@ class _ToDoPageState extends State { orderNo: order.orderNo ?? 0, projectID: order.projectID ?? 0, projectName: order.projectName ?? "", + setupID: order.setupID ?? "0", ), ), ); diff --git a/lib/splashPage.dart b/lib/splashPage.dart index a93f75a1..2edf222e 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -60,9 +60,9 @@ class _SplashScreenState extends State { ZoomService().initializeZoomSDK(); - // if (!kDebugMode) { - // _initializeClarity(); - // } + if (!kDebugMode) { + _initializeClarity(); + } if (isAppOpenedFromCall) { navigateToTeleConsult(); @@ -87,7 +87,7 @@ class _SplashScreenState extends State { void _initializeClarity() { final config = ClarityConfig( 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); diff --git a/lib/widgets/app_language_change.dart b/lib/widgets/app_language_change.dart index b084870b..ff8f9c59 100644 --- a/lib/widgets/app_language_change.dart +++ b/lib/widgets/app_language_change.dart @@ -86,6 +86,9 @@ class _AppLanguageChangeState extends State { //Reload Emergency Services Data emergencyServicesViewModel.checkPatientERAdvanceBalance(); + todoSectionViewModel.setIsAncillaryOrdersNeedReloading(true); + todoSectionViewModel.initializeTodoSectionViewModel(); + Navigator.pop(context); }); }), diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index 4074bd19..6ab2b439 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -79,7 +79,7 @@ class ImageOptions { } }, onFilesTap: () async { - FilePickerResult? result = await FilePicker.pickFiles( + FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: [ 'jpg', diff --git a/pubspec.yaml b/pubspec.yaml index ba97f9fb..880ec18e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,8 +2,8 @@ name: hmg_patient_app_new description: "New HMG Patient App" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -#version: 0.0.32+31 -version: 0.0.5+2 +version: 0.0.37+36 +#version: 0.0.6+3 environment: sdk: ">=3.6.0 <4.0.0" @@ -71,8 +71,8 @@ dependencies: firebase_analytics: ^11.5.1 firebase_crashlytics: ^4.3.8 jiffy: ^6.4.3 - # hijri_gregorian_calendar: - # path: /Users/aamir/StudioProjects/hijri_gregorian_calendar +# hijri_gregorian_calendar: +# path: /Users/aamir/StudioProjects/hijri_gregorian_calendar sms_otp_auto_verify: ^2.2.0 web: any flutter_staggered_animations: ^1.1.1 @@ -89,7 +89,7 @@ dependencies: path: package/flutter_swiper_view flutter_callkit_incoming: ^3.0.0 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_platform_interface: ^2.10.0 @@ -106,6 +106,7 @@ dependencies: flutter_paytabs_bridge: ^2.7.13 clarity_flutter: 1.9.0 + flutter_amazonpaymentservices: ^0.0.13 screen_brightness: ^1.0.1 dev_dependencies: @@ -179,4 +180,3 @@ flutter: -# SA8345000000815089206001 \ No newline at end of file