From 75c55db90f174f9acd3925b744b5149cf80e3bae Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Wed, 15 Jul 2026 11:15:35 +0300 Subject: [PATCH 01/17] notification center-id --- lib/core/utils/request_utils.dart | 7 +++++++ .../request_models/send_activation_request_model.dart | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/core/utils/request_utils.dart b/lib/core/utils/request_utils.dart index 4d4ae4b5..4e5293a7 100644 --- a/lib/core/utils/request_utils.dart +++ b/lib/core/utils/request_utils.dart @@ -192,6 +192,13 @@ class RequestUtils { request.familyRegionID = zipCode == CountryEnum.saudiArabia.countryCode ? 1 : 2; } + request.notificationCenterAppGUID = ApiConsts.notificationCenterAppGUID; + request.notificationCenterAppServiceGUID = isFormFamilyFile + ? ApiConsts.notificationServiceGUID_FamilyFile + : isForRegister + ? ApiConsts.notificationServiceGUID_Registration + : ApiConsts.notificationServiceGUID_Login; + return request; } diff --git a/lib/features/authentication/models/request_models/send_activation_request_model.dart b/lib/features/authentication/models/request_models/send_activation_request_model.dart index 2908f7ec..76d6c939 100644 --- a/lib/features/authentication/models/request_models/send_activation_request_model.dart +++ b/lib/features/authentication/models/request_models/send_activation_request_model.dart @@ -30,6 +30,8 @@ class SendActivationRequest { int? status; int? familyRegionID; bool? isPatientExcluded; + String? notificationCenterAppGUID; + String? notificationCenterAppServiceGUID; SendActivationRequest( {this.patientMobileNumber, @@ -62,7 +64,9 @@ class SendActivationRequest { this.responseID, this.status, this.familyRegionID, - this.isPatientExcluded}); + this.isPatientExcluded, + this.notificationCenterAppGUID, + this.notificationCenterAppServiceGUID}); SendActivationRequest.fromJson(Map json) { patientMobileNumber = json['PatientMobileNumber']; @@ -96,6 +100,8 @@ class SendActivationRequest { status = json['Status']; familyRegionID = json['FamilyRegionID']; isPatientExcluded = json['IsPatientExcluded']; + notificationCenterAppGUID = json['NotificationCenterAppGUID']; + notificationCenterAppServiceGUID = json['NotificationCenterAppSeviceGUID']; } Map toJson() { @@ -135,6 +141,8 @@ class SendActivationRequest { data['Status'] = status; data['FamilyRegionID'] = familyRegionID; data['IsPatientExcluded'] = isPatientExcluded; + data['NotificationCenterAppGUID'] = notificationCenterAppGUID; + data['NotificationCenterAppSeviceGUID'] = notificationCenterAppServiceGUID; return data; } } From f860fa9b14243f2021e5e695ab10a40aba33b761 Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Tue, 29 Sep 2026 11:12:51 +0300 Subject: [PATCH 02/17] remove the notification id send on checkactivation code --- lib/features/authentication/authentication_repo.dart | 2 ++ lib/features/authentication/authentication_view_model.dart | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index 291e4a9b..1c95d99a 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -222,6 +222,8 @@ class AuthenticationRepoImp implements AuthenticationRepo { if (isRegister) { newRequest["activationCode"] = activationCode ?? "0000"; newRequest["isSilentLogin"] = activationCode == null; + newRequest.remove("NotificationCenterAppGUID"); + newRequest.remove("NotificationCenterAppSeviceGUID"); } else { newRequest.activationCode = activationCode ?? "0000"; newRequest.isSilentLogin = activationCode == null; diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 271c628d..57f76b66 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -1517,7 +1517,9 @@ class AuthenticationViewModel extends ChangeNotifier { LoaderBottomSheet.hideLoader(); if (apiResponse.data["MessageStatus"] == 1) { LoaderBottomSheet.showLoader(loadingText: "Your medical file has been created successfully. \nPlease proceed to login."); - //TODO: Here We Need to Show a Dialog Of Something in the case of Success. + // Reset registration payload and clear healthId so subsequent logins hit the login endpoint + _appState.getUserRegistrationPayload.healthId = null; + _appState.setUserRegistrationPayload = RegistrationDataModelPayload(); await clearDefaultInputValues(); // This will Clear All Default Values Of User. Future.delayed(Duration(seconds: 1), () { LoaderBottomSheet.hideLoader(); From fb37d694117b49fb468d3002f2e9e13f0e600ac6 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 29 Sep 2026 12:57:12 +0300 Subject: [PATCH 03/17] Update to stores, VersionID 22.3 --- assets/images/svg/Icon-migrate.svg | 10 ++ assets/langs/ar-SA.json | 6 +- assets/langs/en-US.json | 6 +- lib/app_closed_page.dart | 104 ++++++++++++++++++ lib/core/api/api_client.dart | 2 +- lib/core/api_consts.dart | 6 +- lib/core/app_assets.dart | 1 + .../authentication_view_model.dart | 9 +- lib/generated/locale_keys.g.dart | 4 + lib/splashPage.dart | 16 ++- 10 files changed, 148 insertions(+), 16 deletions(-) create mode 100644 assets/images/svg/Icon-migrate.svg create mode 100644 lib/app_closed_page.dart diff --git a/assets/images/svg/Icon-migrate.svg b/assets/images/svg/Icon-migrate.svg new file mode 100644 index 00000000..b8dc647f --- /dev/null +++ b/assets/images/svg/Icon-migrate.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 911856ab..a09efff5 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -2050,5 +2050,9 @@ "liveCareTermsConditions46": "13-3\tإذا تبين أن أي بند أو جزء من أي بند من هذه الشروط والأحكام باطل أو غير قابل للإنفاذ أو غير صالح، فسيتم فصله عن هذه الشروط والأحكام، تاركاً باقي الشروط سارية المفعول بكامل الأثر والنفاذ، بشرط ألا يغير فصل ذلك البند من الطبيعة الأساسية لهذه الشروط والأحكام.", "liveCareTermsHeading16": "14.\tالشكاوى", "liveCareTermsConditions47": "خدمات الرعاية الصحية عن بُعد الخاصة بنا سوف تكون لتخصصات طبية محددة أو لمواعيد المتابعة أو إعادة صرف الدواء. ", - "liveCareTermsConditions48": "إذا كانت لديك أي شكاوى أو مخاوف بشأن التطبيق و/أو موقع الويب أو خدماتنا أو كيفية تعاملنا مع معلوماتك الشخصية، فيرجى التواصل معنا على: EServices.HMG@drsulaimanalhabib.com أو الاتصال على الرقم: 9553 525 011" + "liveCareTermsConditions48": "إذا كانت لديك أي شكاوى أو مخاوف بشأن التطبيق و/أو موقع الويب أو خدماتنا أو كيفية تعاملنا مع معلوماتك الشخصية، فيرجى التواصل معنا على: EServices.HMG@drsulaimanalhabib.com أو الاتصال على الرقم: 9553 525 011", + "importantNotice": "تنبيه هام", + "appDecommissionedText": "تم إيقاف هذا الإصدار من تطبيق الحبيب الطبي.\nللاستمرار في الاستفادة من الخدمات الصحية والسجلات الطبية، يرجى تحميل التطبيق الجديد.", + "appDecommissionedSubText": "نعتذر بشدة عن الإزعاج الناتج عن هذا الانتقال.", + "downloadApp": "حمل التطبيق" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index aaafb124..d7cd2411 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -2040,7 +2040,11 @@ "liveCareTermsConditions46": "13.3 If any clause or part of any clause of these Terms & Conditions is found to be void, unenforceable or invalid, then it will be severed from these Terms & Conditions, leaving the remainder in full force and effect, provided that the severance has not altered the basic nature of these Terms & Conditions. ", "liveCareTermsHeading16": "14. COMPLAINTS", "liveCareTermsConditions47": "Our Telehealth Services will be for specific medical specialties or follow-up or medication refill appointments.", - "liveCareTermsConditions48": "If you have any complaints or concerns about the Application and or the Website, Our Services, or how we handle your personal information please contact us on: EServices.HMG@drsulaimanalhabib.com or call 011 525 9553" + "liveCareTermsConditions48": "If you have any complaints or concerns about the Application and or the Website, Our Services, or how we handle your personal information please contact us on: EServices.HMG@drsulaimanalhabib.com or call 011 525 9553", + "importantNotice": "Important Notice", + "appDecommissionedText": "This version of our healthcare application has been officially discontinued. To ensure uninterrupted access to your clinical records, we are transitioning all patients back to our main platform.", + "appDecommissionedSubText": "We sincerely apologize for the inconvenience of this transition.", + "downloadApp": "Download App" } diff --git a/lib/app_closed_page.dart b/lib/app_closed_page.dart new file mode 100644 index 00000000..2cda8935 --- /dev/null +++ b/lib/app_closed_page.dart @@ -0,0 +1,104 @@ +import 'dart:io'; + +import 'package:clarity_flutter/clarity_flutter.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:freerasp/freerasp.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/services/security_service.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; + +import 'generated/locale_keys.g.dart'; +import 'widgets/buttons/custom_button.dart'; + +class AppClosedPage extends StatefulWidget { + const AppClosedPage({super.key}); + + @override + State createState() => _AppClosedPageState(); +} + +class _AppClosedPageState extends State { + @override + void initState() { + // Clarity.setCurrentScreenName('Unsafe Device Page'); + Talsec.instance.detachListener(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.whiteColor, + body: SafeArea( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24.w), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Logo + Utils.buildImgWithAssets(icon: AppAssets.hmgLogo, width: MediaQuery.of(context).size.width * 0.7, height: 90.h, fit: BoxFit.contain), + SizedBox(height: 32.h), + Text( + LocaleKeys.importantNotice.tr(context: context), + style: TextStyle( + fontSize: 28.f, + fontWeight: FontWeight.bold, + color: AppColors.textColor, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 16.h), + // Description + Text( + LocaleKeys.appDecommissionedText.tr(context: context), + style: TextStyle( + fontSize: 14.f, + color: Colors.black87, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 24.h), + Text( + LocaleKeys.appDecommissionedSubText.tr(context: context), + style: TextStyle(fontSize: 14.f, color: AppColors.textColorLight, fontStyle: FontStyle.italic), + textAlign: TextAlign.center, + ), + SizedBox(height: 48.h), + CustomButton( + text: LocaleKeys.downloadApp.tr(context: context), + onPressed: () { + if (Platform.isAndroid) { + Utils.openWebView( + url: "https://play.google.com/store/apps/details?id=com.ejada.hmg", + ); + } + if (Platform.isIOS) { + Utils.openWebView( + url: "https://itunes.apple.com/app/id733503978", + ); + } + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + fontSize: (isFoldable || isTablet) ? 12.f : 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + // height: isTablet || isFoldable ? 46.h : 40.h, + height: 56.h, + iconColor: AppColors.primaryRedColor, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 7a6096bc..00eebb34 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -209,7 +209,7 @@ class ApiClientImp implements ApiClient { body['TokenID'] = "@dm!n"; } - // body['TokenID'] = "@dm!n"; + body['TokenID'] = "@dm!n"; // body['PatientID'] = 7000792; // body['PatientID'] = 53320; // body['PatientTypeID'] = 1; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 44a105b9..daa51bba 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -6,7 +6,7 @@ import 'package:myfatoorah_flutter/myfatoorah_flutter.dart'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT @@ -316,7 +316,7 @@ class ApiConsts { static String googleCloudStorageENTranslationFileBaseURL = "https://storage.googleapis.com/hmg-patientapp-translations"; // ************ static values for Api **************** - static final double appVersionID = 22.1; + static final double appVersionID = 22.3; // static final double appVersionID = 50.7; static final int appChannelId = 3; @@ -386,8 +386,6 @@ var DRIVER_LOCATION = 'Services/Patients.svc/REST/PatientER_GetDriverLocation'; //weather var WEATHER_INDICATOR = 'Services/Weather.svc/REST/GetCityInfo'; -var GET_PRIVILEGE = 'Services/Patients.svc/REST/Service_Privilege'; - // Wifi Credentials var WIFI_CREDENTIALS = "Services/Patients.svc/Hmg_SMS_Get_By_ProjectID_And_PatientID"; diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index bd850985..bd6d7f87 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -385,6 +385,7 @@ class AppAssets { static const String hmg_pharmacy_logo = '$svgBasePath/hmg_pharmacy_logo.svg'; static const String redeem_icon = '$svgBasePath/redeem_icon.svg'; static const String expired_icon = '$svgBasePath/expired_icon.svg'; + static const String icon_migrate = '$svgBasePath/Icon-migrate.svg'; //Health Calculators static const String bodyfatcalc = '$svgBasePath/bfc.svg'; diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 271c628d..d448c88b 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show rootBundle; import 'package:get_it/get_it.dart'; +import 'package:hmg_patient_app_new/app_closed_page.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/cache_consts.dart'; @@ -1827,10 +1828,10 @@ class AuthenticationViewModel extends ChangeNotifier { _appState.setNHICUserData = CheckUserStatusResponseNHIC(); _appState.setUserRegistrationPayload = RegistrationDataModelPayload(); // Clear privilege lists - _appState.setPrivilegeModelList([]); - _appState.setVidaPlusProjectList([]); - _appState.setHMCProjectList([]); - _appState.setProjectsDetailList([]); + // _appState.setPrivilegeModelList([]); + // _appState.setVidaPlusProjectList([]); + // _appState.setHMCProjectList([]); + // _appState.setProjectsDetailList([]); _appState.clearUserSwitchPayloads(); await clearDefaultInputValues(); _navigationService.pushAndReplace(AppRoutes.landingScreen); diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 7dbcfd75..67aacc0e 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -2032,5 +2032,9 @@ abstract class LocaleKeys { static const liveCareTermsHeading16 = 'liveCareTermsHeading16'; static const liveCareTermsConditions47 = 'liveCareTermsConditions47'; static const liveCareTermsConditions48 = 'liveCareTermsConditions48'; + static const importantNotice = 'importantNotice'; + static const appDecommissionedText = 'appDecommissionedText'; + static const appDecommissionedSubText = 'appDecommissionedSubText'; + static const downloadApp = 'downloadApp'; } diff --git a/lib/splashPage.dart b/lib/splashPage.dart index cf407d41..8ebe655e 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_callkit_incoming/entities/call_event.dart'; import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; import 'package:get_it/get_it.dart'; +import 'package:hmg_patient_app_new/app_closed_page.dart'; import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -29,6 +30,7 @@ import 'package:hmg_patient_app_new/services/notification_service.dart'; import 'package:hmg_patient_app_new/services/zoom_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/unsafe_device.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:lottie/lottie.dart'; import 'package:safe_device/safe_device.dart'; @@ -91,12 +93,16 @@ class _SplashScreenState extends State { if (isAppOpenedFromCall) { navigateToTeleConsult(); } else { - if (await Utils.getBoolFromPrefs(CacheConst.firstLaunch)) { - // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: OnboardingScreen()))); - Navigator.of(getIt.get().navigatorKey.currentContext!).pushReplacement(FadePage(page: OnboardingScreen())); + if (Utils.havePrivilege(124)) { + Navigator.of(getIt.get().navigatorKey.currentContext!).pushAndRemoveUntil(CustomPageRoute(page: AppClosedPage()), (r) => false); } else { - // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation()))); - Navigator.of(getIt.get().navigatorKey.currentContext!).pushReplacement(FadePage(page: LandingNavigation())); + if (await Utils.getBoolFromPrefs(CacheConst.firstLaunch)) { + // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: OnboardingScreen()))); + Navigator.of(getIt.get().navigatorKey.currentContext!).pushReplacement(FadePage(page: OnboardingScreen())); + } else { + // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation()))); + Navigator.of(getIt.get().navigatorKey.currentContext!).pushReplacement(FadePage(page: LandingNavigation())); + } } } } From d3a799de7176956db9c7f3029812ca42032c64fb Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 29 Sep 2026 15:26:14 +0300 Subject: [PATCH 04/17] Auth update --- lib/features/authentication/authentication_view_model.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index d448c88b..ae984002 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -1827,11 +1827,13 @@ class AuthenticationViewModel extends ChangeNotifier { _appState.setSelectDeviceByImeiRespModelElement(null); _appState.setNHICUserData = CheckUserStatusResponseNHIC(); _appState.setUserRegistrationPayload = RegistrationDataModelPayload(); + // Clear privilege lists // _appState.setPrivilegeModelList([]); // _appState.setVidaPlusProjectList([]); // _appState.setHMCProjectList([]); // _appState.setProjectsDetailList([]); + _appState.clearUserSwitchPayloads(); await clearDefaultInputValues(); _navigationService.pushAndReplace(AppRoutes.landingScreen); From f04d8bd56aff6fc761602e78acb7d8db43a23924 Mon Sep 17 00:00:00 2001 From: Haroon Amjad <> Date: Tue, 29 Sep 2026 16:10:13 +0300 Subject: [PATCH 05/17] Updates --- lib/splashPage.dart | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/lib/splashPage.dart b/lib/splashPage.dart index 8ebe655e..0b43ae63 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -93,19 +93,19 @@ class _SplashScreenState extends State { if (isAppOpenedFromCall) { navigateToTeleConsult(); } else { - if (Utils.havePrivilege(124)) { - Navigator.of(getIt.get().navigatorKey.currentContext!).pushAndRemoveUntil(CustomPageRoute(page: AppClosedPage()), (r) => false); + // if (Utils.havePrivilege(124)) { + // Navigator.of(getIt.get().navigatorKey.currentContext!).pushAndRemoveUntil(CustomPageRoute(page: AppClosedPage()), (r) => false); + // } else { + if (await Utils.getBoolFromPrefs(CacheConst.firstLaunch)) { + // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: OnboardingScreen()))); + Navigator.of(getIt.get().navigatorKey.currentContext!).pushReplacement(FadePage(page: OnboardingScreen())); } else { - if (await Utils.getBoolFromPrefs(CacheConst.firstLaunch)) { - // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: OnboardingScreen()))); - Navigator.of(getIt.get().navigatorKey.currentContext!).pushReplacement(FadePage(page: OnboardingScreen())); - } else { - // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation()))); - Navigator.of(getIt.get().navigatorKey.currentContext!).pushReplacement(FadePage(page: LandingNavigation())); - } + // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation()))); + Navigator.of(getIt.get().navigatorKey.currentContext!).pushReplacement(FadePage(page: LandingNavigation())); } } } + // } }); // var zoom = ZoomVideoSdk(); @@ -127,24 +127,24 @@ class _SplashScreenState extends State { // Set initial app context tags try { final appState = getIt.get(); - + // Set device and app metadata Clarity.setCustomTag('app_version', ApiConsts.appVersionID.toString()); Clarity.setCustomTag('device_type', appState.deviceTypeID ?? 'unknown'); Clarity.setCustomTag('platform', Platform.isIOS ? 'ios' : 'android'); - + // Set user context if authenticated if (appState.isAuthenticated) { final user = appState.getAuthenticatedUser(); if (user != null) { // Set user ID for session tracking Clarity.setCustomUserId(user.patientId.toString()); - + // Set custom tags for filtering sessions Clarity.setCustomTag('user_type', user.patientType?.toString() ?? 'unknown'); Clarity.setCustomTag('user_segment', user.outSa == 0 ? 'in_sa' : 'out_sa'); Clarity.setCustomTag('auth_status', 'authenticated'); - + // Optional: Set other non-sensitive user properties if (user.mobileNumber != null) { // Mask phone number for privacy (show only last 4 digits) @@ -155,10 +155,9 @@ class _SplashScreenState extends State { } else { Clarity.setCustomTag('auth_status', 'guest'); } - + // Send initialization event Clarity.sendCustomEvent('app_initialized'); - } catch (e) { debugPrint('Failed to set Clarity user context: $e'); } @@ -171,8 +170,7 @@ class _SplashScreenState extends State { // GetIt.instance().remove(key: CacheConst.isAppOpenedFromCall); Utils.removeFromPrefs(CacheConst.isAppOpenedFromCall); - Navigator.of(GetIt.instance().navigatorKey.currentContext!) - .pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation()))); + Navigator.of(GetIt.instance().navigatorKey.currentContext!).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation()))); Navigator.pushReplacementNamed( // context, GetIt.instance().navigatorKey.currentContext!, @@ -392,8 +390,6 @@ class _SplashScreenState extends State { Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.whiteColor, - body: Lottie.asset(AppAnimations.loadingAnimation, - repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.fill) - .center); + body: Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.fill).center); } } From b797334590c347c2a2ad69e79030854b56d8546a Mon Sep 17 00:00:00 2001 From: Haroon Amjad <> Date: Wed, 30 Sep 2026 01:32:34 +0300 Subject: [PATCH 06/17] Update to stores, VersionID 21.4 --- android/build.gradle.kts | 9 +++++++ .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle.kts | 20 +++++++++++++++ lib/core/api/api_client.dart | 2 +- lib/core/api_consts.dart | 2 +- .../wallet_payment_confirm_page.dart | 25 ------------------- lib/services/localauth_service.dart | 10 +++++--- lib/services/security_service.dart | 2 +- lib/splashPage.dart | 4 --- pubspec.yaml | 16 ++++++------ 10 files changed, 48 insertions(+), 44 deletions(-) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index b703166b..29a4e9da 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -63,6 +63,15 @@ allprojects { } subprojects { + project.buildscript.repositories { + google() + mavenCentral() + } + + project.repositories { + google() + mavenCentral() + } afterEvaluate { if (project.hasProperty("android")) { project.extensions.findByName("android")?.let { androidExt -> diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index fe111165..4ee62d8f 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists #distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index c6e1e3b1..434a4219 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -16,6 +16,26 @@ pluginManagement { } } +dependencyResolutionManagement { +// repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + repositoriesMode.set(RepositoriesMode.PREFER_PROJECT) + repositories { + google() + mavenCentral() + maven { + url = uri("https://storage.googleapis.com/download.flutter.io") + } + // Huawei HMS repo + maven { + url = uri("https://developer.huawei.com/repo/") + } + // Mapbox repo + maven { + url = uri("https://api.mapbox.com/downloads/v2/releases/maven") + } + } +} + plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" // id("com.android.application") version "8.9.3" apply false diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 7a72e251..07198caa 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -209,7 +209,7 @@ class ApiClientImp implements ApiClient { body['TokenID'] = "@dm!n"; } - body['TokenID'] = "@dm!n"; + // body['TokenID'] = "@dm!n"; // body['PatientID'] = 7000792; // body['PatientID'] = 53320; // body['PatientTypeID'] = 1; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index c7621289..6a65e92f 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -317,7 +317,7 @@ class ApiConsts { // ************ static values for Api **************** // static final double appVersionID = 21.9; - static final double appVersionID = 21.3; + static final double appVersionID = 21.4; // static final double appVersionID = 50.7; static final int appChannelId = 3; diff --git a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart index 52cea368..101e2359 100644 --- a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart +++ b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart @@ -7,7 +7,6 @@ import 'package:clarity_flutter/clarity_flutter.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'; @@ -830,28 +829,4 @@ 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/services/localauth_service.dart b/lib/services/localauth_service.dart index 2875a7fe..cdce9005 100644 --- a/lib/services/localauth_service.dart +++ b/lib/services/localauth_service.dart @@ -17,10 +17,12 @@ class LocalAuthService { final isAuthenticated = await localAuth.authenticate( localizedReason: 'Please authenticate to proceed', - options: const AuthenticationOptions( - biometricOnly: true, - stickyAuth: true, - ), + persistAcrossBackgrounding: false, // Replaces stickyAuth + biometricOnly: true, + // options: const AuthenticationOptions( + // biometricOnly: true, + // stickyAuth: true, + // ), ); return isAuthenticated; diff --git a/lib/services/security_service.dart b/lib/services/security_service.dart index b72dffeb..2af9c45a 100644 --- a/lib/services/security_service.dart +++ b/lib/services/security_service.dart @@ -82,7 +82,7 @@ class SecurityServiceImpl implements SecurityService { onObfuscationIssues: () => _handleThreat('Obfuscation Issues', ThreatSeverity.warning), onDebug: () => _handleThreat('Debug Mode', ThreatSeverity.critical), onDeviceBinding: () => _handleThreat('Device Binding', ThreatSeverity.critical), - onDeviceID: () => _handleThreat('Device ID Mismatch', ThreatSeverity.critical), + onDeviceID: () => _handleThreat('Device ID Mismatch', ThreatSeverity.warning), onHooks: () => _handleThreat('Hooks Detected', ThreatSeverity.critical), onPasscode: () => _handleThreat('Passcode Not Set', ThreatSeverity.warning), onPrivilegedAccess: () => _handleThreat('Privileged Access (Root/Jailbreak)', ThreatSeverity.critical), diff --git a/lib/splashPage.dart b/lib/splashPage.dart index 0b43ae63..b1d03c80 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -93,9 +93,6 @@ class _SplashScreenState extends State { if (isAppOpenedFromCall) { navigateToTeleConsult(); } else { - // if (Utils.havePrivilege(124)) { - // Navigator.of(getIt.get().navigatorKey.currentContext!).pushAndRemoveUntil(CustomPageRoute(page: AppClosedPage()), (r) => false); - // } else { if (await Utils.getBoolFromPrefs(CacheConst.firstLaunch)) { // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: OnboardingScreen()))); Navigator.of(getIt.get().navigatorKey.currentContext!).pushReplacement(FadePage(page: OnboardingScreen())); @@ -105,7 +102,6 @@ class _SplashScreenState extends State { } } } - // } }); // var zoom = ZoomVideoSdk(); diff --git a/pubspec.yaml b/pubspec.yaml index e18f2205..c6bc2c93 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: 4.6.044+1 -version: 4.6.1002+40501002 +#version: 4.6.046+1 +version: 4.6.1004+40501004 environment: sdk: ">=3.6.0 <4.0.0" @@ -17,8 +17,8 @@ dependencies: cached_network_image: ^3.4.1 http: ^1.5.0 flutter_svg: ^2.2.0 - shared_preferences: ^2.5.3 - connectivity_plus: ^6.1.5 + shared_preferences: ^2.5.5 + connectivity_plus: ^7.3.1 auto_size_text: ^3.0.0 shimmer: ^3.0.0 sizer: ^3.1.3 @@ -45,7 +45,7 @@ dependencies: flutter_ios_voip_kit_karmm: ^0.8.0 image_picker: ^1.2.0 file_picker: 10.3.8 - local_auth: ^2.3.0 + local_auth: ^3.0.2 share_plus: ^11.1.0 device_calendar_plus: path: ./package/device_calendar_plus @@ -107,8 +107,10 @@ dependencies: in_app_review: ^2.0.11 flutter_paytabs_bridge: ^2.7.14 - clarity_flutter: 1.9.0 - flutter_amazonpaymentservices: ^0.0.13 + clarity_flutter: ^1.10.0 +# flutter_amazonpaymentservices: ^0.0.13 + flutter_amazonpaymentservices: + path: ./package/flutter_amazonpaymentservices screen_brightness: ^1.0.1 flutter_screenshot_blocker: ^1.0.4 cloudflare_turnstile: ^3.7.2 From 585514576199619c56c25fe7f4de274b20f82ab7 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Wed, 30 Sep 2026 16:49:02 +0300 Subject: [PATCH 07/17] Notification page changes --- assets/langs/ar-SA.json | 4 +- assets/langs/en-US.json | 4 +- .../notifications_view_model.dart | 60 +++++++++++++++---- .../occasion_theme/occasion_config_model.dart | 5 ++ lib/generated/locale_keys.g.dart | 2 + .../hmg_services/services_page.dart | 2 +- lib/presentation/home/landing_page.dart | 1 + .../widgets/medical_report_card.dart | 6 +- .../widgets/patient_sick_leave_card.dart | 2 +- .../medical_report/medical_reports_page.dart | 37 +----------- .../notifications_list_page.dart | 49 +++++++++++++++ 11 files changed, 119 insertions(+), 53 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index a09efff5..8f047535 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -2054,5 +2054,7 @@ "importantNotice": "تنبيه هام", "appDecommissionedText": "تم إيقاف هذا الإصدار من تطبيق الحبيب الطبي.\nللاستمرار في الاستفادة من الخدمات الصحية والسجلات الطبية، يرجى تحميل التطبيق الجديد.", "appDecommissionedSubText": "نعتذر بشدة عن الإزعاج الناتج عن هذا الانتقال.", - "downloadApp": "حمل التطبيق" + "downloadApp": "حمل التطبيق", + "unread": "غير مقروء", + "read": "مقروءة" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index d7cd2411..184fb93e 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -2044,7 +2044,9 @@ "importantNotice": "Important Notice", "appDecommissionedText": "This version of our healthcare application has been officially discontinued. To ensure uninterrupted access to your clinical records, we are transitioning all patients back to our main platform.", "appDecommissionedSubText": "We sincerely apologize for the inconvenience of this transition.", - "downloadApp": "Download App" + "downloadApp": "Download App", + "unread": "Unread", + "read": "Read" } diff --git a/lib/features/notifications/notifications_view_model.dart b/lib/features/notifications/notifications_view_model.dart index 85049ec9..0f3cc160 100644 --- a/lib/features/notifications/notifications_view_model.dart +++ b/lib/features/notifications/notifications_view_model.dart @@ -13,11 +13,16 @@ class NotificationsViewModel extends ChangeNotifier { ErrorHandlerService errorHandlerService; List notificationsList = []; + List readdNotificationsList = []; + List unreadNotificationsList = []; + List allNotificationsList = []; int currentPage = 0; int pagingSize = 50; int notificationStatusID = 2; // Default to status 2 (e.g., unread) + int selectedNotificationsTabIndex = 0; + NotificationsViewModel({ required this.notificationsRepo, required this.errorHandlerService, @@ -35,6 +40,18 @@ class NotificationsViewModel extends ChangeNotifier { notifyListeners(); } + void onNotificationTabChange(int index) { + selectedNotificationsTabIndex = index; + if (index == 0) { + notificationsList = allNotificationsList; + } else if (index == 1) { + notificationsList = unreadNotificationsList; + } else if (index == 2) { + notificationsList = readdNotificationsList; + } + notifyListeners(); + } + setNotificationStatusID(int statusID) { notificationStatusID = statusID; notificationsList.clear(); @@ -49,6 +66,9 @@ class NotificationsViewModel extends ChangeNotifier { }) async { isNotificationsLoading = true; unreadNotificationsCount = 0; + + notificationsList.clear(); + notifyListeners(); final result = await notificationsRepo.getAllNotifications( @@ -80,17 +100,37 @@ class NotificationsViewModel extends ChangeNotifier { hasMoreNotifications = false; } - notificationsList.addAll(newNotifications); - for (var notification in notificationsList) { - if (notification.isRead == false) { - unreadNotificationsCount++; + try { + notificationsList.addAll(newNotifications); + + // allNotificationsList.clear(); + // readdNotificationsList.clear(); + // unreadNotificationsList.clear(); + + // for (var notification in notificationsList) { + // if (notification.isRead == false) { + // unreadNotificationsCount++; + // unreadNotificationsList.add(notification); + // } else { + // readdNotificationsList.add(notification); + // } + // } + + // Filter out the items directly without a loop + unreadNotificationsList = notificationsList.where((n) => n.isRead == false).toList(); + readdNotificationsList = notificationsList.where((n) => n.isRead == true).toList(); + + // Calculate count based on the new list length + unreadNotificationsCount = unreadNotificationsList.length; + + currentPage++; + allNotificationsList = notificationsList; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); } - } - currentPage++; - - notifyListeners(); - if (onSuccess != null) { - onSuccess(apiResponse); + } catch (ex) { + print(ex.toString()); } } }, diff --git a/lib/features/occasion_theme/occasion_config_model.dart b/lib/features/occasion_theme/occasion_config_model.dart index 03bf9392..de8d14ef 100644 --- a/lib/features/occasion_theme/occasion_config_model.dart +++ b/lib/features/occasion_theme/occasion_config_model.dart @@ -68,6 +68,11 @@ class OccasionConfigModel { enabled: (json['enabled'] as bool?) ?? false, startDate: _parseDate(json['start_date']), endDate: _parseDate(json['end_date']), + + // enabled: true, + // startDate: _parseDate("2026-09-30T00:00:00Z"), + // endDate: _parseDate("2026-10-05T00:00:00Z"), + primaryColorHex: json['primary_color'] as String?, backgroundOpacityImage: json['background_opacity_image'] as String?, backgroundImageOpacity: (json['background_image_opacity'] as num?)?.toDouble() ?? 1.0, diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 67aacc0e..9de9ce9f 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -2036,5 +2036,7 @@ abstract class LocaleKeys { static const appDecommissionedText = 'appDecommissionedText'; static const appDecommissionedSubText = 'appDecommissionedSubText'; static const downloadApp = 'downloadApp'; + static const unread = 'unread'; + static const read = 'read'; } diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart index 582fd6fc..34fc268f 100644 --- a/lib/presentation/hmg_services/services_page.dart +++ b/lib/presentation/hmg_services/services_page.dart @@ -72,7 +72,7 @@ class _ServicesPageState extends State { LocaleKeys.emergencyServices.tr(), "", AppAssets.emergency_services_icon, - bgColor: AppColors.primaryRedColor, + bgColor: AppColors.primaryRedColorBase, true, route: null, onTap: () async { // if (getIt.get().isAuthenticated) { diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 8644655d..9d401fac 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -437,6 +437,7 @@ class _LandingPageState extends State { : SizedBox.shrink(), ]).onPress(() async { if (appState.isAuthenticated) { + notificationsViewModel.onNotificationTabChange(0); notificationsViewModel.setNotificationStatusID(2); notificationsViewModel.getAllNotifications(); Navigator.of(context).push( diff --git a/lib/presentation/medical_file/widgets/medical_report_card.dart b/lib/presentation/medical_file/widgets/medical_report_card.dart index 1a43b83b..905823b4 100644 --- a/lib/presentation/medical_file/widgets/medical_report_card.dart +++ b/lib/presentation/medical_file/widgets/medical_report_card.dart @@ -84,15 +84,15 @@ class MedicalReportCard extends StatelessWidget { ), ); }, - backgroundColor: AppColors.lightRedButtonColor, - borderColor: AppColors.lightRedButtonColor, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor, fontSize: 14.f, - // fontWeight: FontWeight.w600, isBold: true, borderRadius: 12.r, height: 40.h, + iconSize: 18.f, ).toShimmer2(isShow: isLoading), ], ), diff --git a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart index f06e6a0e..2bb879b5 100644 --- a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart +++ b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart @@ -122,7 +122,7 @@ class PatientSickLeaveCard extends StatelessWidget { }); }, backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.transparent, textColor: AppColors.primaryRedColor, fontSize: 14.f, isBold: true, diff --git a/lib/presentation/medical_report/medical_reports_page.dart b/lib/presentation/medical_report/medical_reports_page.dart index e1ac3969..64939629 100644 --- a/lib/presentation/medical_report/medical_reports_page.dart +++ b/lib/presentation/medical_report/medical_reports_page.dart @@ -50,42 +50,7 @@ class _MedicalReportsPageState extends State { child: Consumer(builder: (context, medicalFileVM, child) { return Column( children: [ - SizedBox(height: 16.h), - // Status filter tabs - // Row( - // children: [ - // CustomButton( - // text: LocaleKeys.byClinic.tr(context: context), - // onPressed: () { - // medicalFileVM.setIsMedicalReportsSortByClinic(true); - // }, - // backgroundColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, - // borderColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), - // textColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, - // fontSize: 12, - // isBold: true, - // borderRadius: 10, - // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - // height: 40.h, - // ), - // SizedBox(width: 8.h), - // CustomButton( - // text: LocaleKeys.byHospital.tr(context: context), - // onPressed: () { - // medicalFileVM.setIsMedicalReportsSortByClinic(false); - // }, - // backgroundColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, - // borderColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, - // textColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, - // fontSize: 12, - // isBold: true, - // borderRadius: 10, - // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - // height: 40.h, - // ), - // ], - // ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 8.h), + SizedBox(height: 24.h), Row( children: [ CustomButton( diff --git a/lib/presentation/notifications/notifications_list_page.dart b/lib/presentation/notifications/notifications_list_page.dart index 77f9a20a..eb1af696 100644 --- a/lib/presentation/notifications/notifications_list_page.dart +++ b/lib/presentation/notifications/notifications_list_page.dart @@ -17,6 +17,7 @@ import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/notifications/notification_details_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:provider/provider.dart'; import 'package:intl/intl.dart'; import 'dart:ui' as ui; @@ -51,6 +52,54 @@ class _NotificationsListPageState extends State { child: Consumer(builder: (context, notificationsVM, child) { return Column( children: [ + Row( + children: [ + CustomButton( + text: LocaleKeys.all.tr(context: context), + onPressed: () { + notificationsVM.onNotificationTabChange(0); + }, + backgroundColor: notificationsVM.selectedNotificationsTabIndex == 0 ? AppColors.bgRedLightColor : AppColors.whiteColor, + borderColor: notificationsVM.selectedNotificationsTabIndex == 0 ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), + textColor: notificationsVM.selectedNotificationsTabIndex == 0 ? AppColors.primaryRedColor : AppColors.blackColor, + fontSize: 12, + isBold: true, + borderRadius: 10, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + SizedBox(width: 8.h), + CustomButton( + text: LocaleKeys.unread.tr(context: context), + onPressed: () { + notificationsVM.onNotificationTabChange(1); + }, + backgroundColor: notificationsVM.selectedNotificationsTabIndex == 1 ? AppColors.bgRedLightColor : AppColors.whiteColor, + borderColor: notificationsVM.selectedNotificationsTabIndex == 1 ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), + textColor: notificationsVM.selectedNotificationsTabIndex == 1 ? AppColors.primaryRedColor : AppColors.blackColor, + fontSize: 12, + isBold: true, + borderRadius: 10, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + SizedBox(width: 8.h), + CustomButton( + text: LocaleKeys.read.tr(context: context), + onPressed: () { + notificationsVM.onNotificationTabChange(2); + }, + backgroundColor: notificationsVM.selectedNotificationsTabIndex == 2 ? AppColors.bgRedLightColor : AppColors.whiteColor, + borderColor: notificationsVM.selectedNotificationsTabIndex == 2 ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), + textColor: notificationsVM.selectedNotificationsTabIndex == 2 ? AppColors.primaryRedColor : AppColors.blackColor, + fontSize: 12, + isBold: true, + borderRadius: 10, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + ], + ).paddingSymmetrical(24.h, 0.h), Container( margin: EdgeInsets.symmetric(vertical: 24.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration( From 1eb0d9628629d9c68acda12061bc52ab4e5cf5f3 Mon Sep 17 00:00:00 2001 From: Haroon Amjad <> Date: Sun, 4 Oct 2026 11:23:44 +0300 Subject: [PATCH 08/17] updates --- lib/core/api_consts.dart | 2 +- .../livecare/immediate_livecare_payment_page.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 6a65e92f..85de77e5 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -317,7 +317,7 @@ class ApiConsts { // ************ static values for Api **************** // static final double appVersionID = 21.9; - static final double appVersionID = 21.4; + static final double appVersionID = 21.5; // static final double appVersionID = 50.7; static final int appChannelId = 3; 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 2147cbd6..9d30f976 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart @@ -759,8 +759,8 @@ class _ImmediateLiveCarePaymentPageState extends State Date: Mon, 5 Oct 2026 09:53:19 +0300 Subject: [PATCH 09/17] updates --- lib/core/api_consts.dart | 4 ++-- lib/features/occasion_theme/occasion_config_model.dart | 2 +- .../livecare/immediate_livecare_payment_page.dart | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index daa51bba..4b2cd1c1 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -6,7 +6,7 @@ import 'package:myfatoorah_flutter/myfatoorah_flutter.dart'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.preProd; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT @@ -316,7 +316,7 @@ class ApiConsts { static String googleCloudStorageENTranslationFileBaseURL = "https://storage.googleapis.com/hmg-patientapp-translations"; // ************ static values for Api **************** - static final double appVersionID = 22.3; + static final double appVersionID = 22.4; // static final double appVersionID = 50.7; static final int appChannelId = 3; diff --git a/lib/features/occasion_theme/occasion_config_model.dart b/lib/features/occasion_theme/occasion_config_model.dart index de8d14ef..bf9d713f 100644 --- a/lib/features/occasion_theme/occasion_config_model.dart +++ b/lib/features/occasion_theme/occasion_config_model.dart @@ -71,7 +71,7 @@ class OccasionConfigModel { // enabled: true, // startDate: _parseDate("2026-09-30T00:00:00Z"), - // endDate: _parseDate("2026-10-05T00:00:00Z"), + // endDate: _parseDate("2026-10-07T00:00:00Z"), primaryColorHex: json['primary_color'] as String?, backgroundOpacityImage: json['background_opacity_image'] as String?, 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 2147cbd6..9d30f976 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart @@ -759,8 +759,8 @@ class _ImmediateLiveCarePaymentPageState extends State Date: Mon, 5 Oct 2026 10:00:35 +0300 Subject: [PATCH 10/17] updates --- android/app/proguard-rules.pro | 2 +- android/gradle.properties | 1 + ios/Podfile | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index e4e65227..68b506a1 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -3,7 +3,7 @@ -keep class com.twilio.common.** { *; } -keepattributes InnerClasses --keep class com.ejada.** { *; } +#-keep class com.ejada.** { *; } -keep class org.webrtc.** { *; } -keep class com.builttoroam.devicecalendar.** { *; } diff --git a/android/gradle.properties b/android/gradle.properties index f47e3b3a..210295ee 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,6 @@ #org.gradle.jvmargs=-xmx4608m android.enableR8=true +android.enableR8.fullMode=true android.enableJetifier=true android.useDeprecatedNdk=true org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:MaxMetaspaceSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 diff --git a/ios/Podfile b/ios/Podfile index a74c7fb2..e0283052 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -59,7 +59,7 @@ post_install do |installer| 'PERMISSION_NOTIFICATIONS=1', ] build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' - build_configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' + build_configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0' build_configuration.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES' xcconfig_path = build_configuration.base_configuration_reference.real_path xcconfig = File.read(xcconfig_path) From 11370ae860db2fa5008c8e1c6f37ec14d50071cd Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Mon, 5 Oct 2026 10:49:56 +0300 Subject: [PATCH 11/17] In App Browser fixes --- lib/core/api_consts.dart | 2 +- lib/widgets/in_app_browser/InAppBrowser.dart | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 4b2cd1c1..89ab00e3 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -6,7 +6,7 @@ import 'package:myfatoorah_flutter/myfatoorah_flutter.dart'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.preProd; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/widgets/in_app_browser/InAppBrowser.dart b/lib/widgets/in_app_browser/InAppBrowser.dart index fe08fbbe..5eaa313c 100644 --- a/lib/widgets/in_app_browser/InAppBrowser.dart +++ b/lib/widgets/in_app_browser/InAppBrowser.dart @@ -307,12 +307,13 @@ class MyInAppBrowser extends InAppBrowser { form = form.replaceFirst('CUSTNAME_VALUE', patientName); form = form.replaceFirst('CUSTID_VALUE', patientID.toString()); + form = form.replaceFirst('APPOINTMENT_DATE_VALUE', appoDate); + form = form.replaceFirst('APPOINTMENT_NO_VALUE', appoNo.toString()); + form = form.replaceFirst('DOCTOR_ID_VALUE', doctorID.toString()); + form = form.replaceFirst('CLINIC_ID_VALUE', clinicID.toString()); + if (isLiveCareAppo) { form = form.replaceFirst('IS_SCHEDULE_VALUE', "true"); - form = form.replaceFirst('APPOINTMENT_DATE_VALUE', appoDate); - form = form.replaceFirst('APPOINTMENT_NO_VALUE', appoNo.toString()); - form = form.replaceFirst('DOCTOR_ID_VALUE', doctorID.toString()); - form = form.replaceFirst('CLINIC_ID_VALUE', clinicID.toString()); } var bytes = utf8.encode(form); @@ -363,6 +364,10 @@ class MyInAppBrowser extends InAppBrowser { '' + '' + '' + + '' + + '' + + '' + + '' + '' + '' + '' + From d9ad16f9d78829654e13362d488112e0b54a5295 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Mon, 5 Oct 2026 11:35:28 +0300 Subject: [PATCH 12/17] CR 22303 Medical Report implemented --- assets/langs/ar-SA.json | 3 ++- assets/langs/en-US.json | 3 ++- .../medical_file/medical_file_view_model.dart | 13 +++++++++++++ lib/generated/locale_keys.g.dart | 1 + .../appointments/appointment_payment_page.dart | 3 ++- .../appointments/widgets/appointment_card.dart | 12 +++++++++++- .../medical_report/medical_report_request_page.dart | 9 ++++++--- .../medical_report/medical_reports_page.dart | 1 + 8 files changed, 38 insertions(+), 7 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 8f047535..51cc0ec3 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -2056,5 +2056,6 @@ "appDecommissionedSubText": "نعتذر بشدة عن الإزعاج الناتج عن هذا الانتقال.", "downloadApp": "حمل التطبيق", "unread": "غير مقروء", - "read": "مقروءة" + "read": "مقروءة", + "pendingMedicalReportRequest": "لديك بالفعل طلب معلّق للحصول على تقرير طبي خاص بهذا الموعد. يرجى الانتظار حتى اكتمال الطلب قبل تقديم طلب جديد." } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 184fb93e..ceaf6579 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -2046,7 +2046,8 @@ "appDecommissionedSubText": "We sincerely apologize for the inconvenience of this transition.", "downloadApp": "Download App", "unread": "Unread", - "read": "Read" + "read": "Read", + "pendingMedicalReportRequest": "You already have a pending medical report request for this appointment. Please wait for the request to be completed before submitting a new one.", } diff --git a/lib/features/medical_file/medical_file_view_model.dart b/lib/features/medical_file/medical_file_view_model.dart index 29f653ec..f3a4e2e8 100644 --- a/lib/features/medical_file/medical_file_view_model.dart +++ b/lib/features/medical_file/medical_file_view_model.dart @@ -864,4 +864,17 @@ class MedicalFileViewModel extends ChangeNotifier { print("❌ Error updating family member profile image in cache: $e"); } } + + bool checkExistingMedicalReportStatus(PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel) { + bool hasExistingRequest = false; + + for (var report in patientMedicalReportRequestedList) { + if (report.clinicID == patientAppointmentHistoryResponseModel.clinicID && report.doctorID == patientAppointmentHistoryResponseModel.doctorID) { + hasExistingRequest = true; + break; + } + } + + return hasExistingRequest; + } } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 9de9ce9f..30e3bc0a 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -2038,5 +2038,6 @@ abstract class LocaleKeys { static const downloadApp = 'downloadApp'; static const unread = 'unread'; static const read = 'read'; + static const pendingMedicalReportRequest = 'pendingMedicalReportRequest'; } diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index ffa9b723..5b7e4c80 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -748,7 +748,8 @@ class _AppointmentPaymentPageState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - (myAppointmentsVM.patientAppointmentShareResponseModel!.isCash ?? true) + // (myAppointmentsVM.patientAppointmentShareResponseModel!.isCash ?? true) + (true) ? Container( height: 50.h, decoration: ShapeDecoration( diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 1eeb842c..b9a2f26f 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -448,7 +448,17 @@ class _AppointmentCardState extends State { if (widget.isForFeedback) { widget.contactUsViewModel!.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel); } else { - widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel); + if(!widget.medicalFileViewModel!.checkExistingMedicalReportStatus(widget.patientAppointmentHistoryResponseModel)) { + widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel); + } else { + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: LocaleKeys.pendingMedicalReportRequest.tr(context: context)), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } } Navigator.pop(context, false); } else { diff --git a/lib/presentation/medical_report/medical_report_request_page.dart b/lib/presentation/medical_report/medical_report_request_page.dart index 47a2df32..859a6b2f 100644 --- a/lib/presentation/medical_report/medical_report_request_page.dart +++ b/lib/presentation/medical_report/medical_report_request_page.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; @@ -26,8 +27,9 @@ class MedicalReportRequestPage extends StatelessWidget { isClose: true, child: Column( children: [ - ListView.separated( - padding: EdgeInsets.only(top: 24.h), + medicalFileViewModel.patientMedicalReportAppointmentHistoryList.isNotEmpty + ? ListView.separated( + padding: EdgeInsets.only(top: 24.h), shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: medicalFileViewModel.patientMedicalReportAppointmentHistoryList.length, @@ -57,7 +59,8 @@ class MedicalReportRequestPage extends StatelessWidget { ); }, separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), - ), + ) + : Utils.getNoDataWidget(context), SizedBox(height: 24.h), ], ), diff --git a/lib/presentation/medical_report/medical_reports_page.dart b/lib/presentation/medical_report/medical_reports_page.dart index 64939629..33f9e6e3 100644 --- a/lib/presentation/medical_report/medical_reports_page.dart +++ b/lib/presentation/medical_report/medical_reports_page.dart @@ -369,6 +369,7 @@ class _MedicalReportsPageState extends State { showCommonBottomSheetWithoutHeight(context, child: Utils.getSuccessWidget(loadingText: LocaleKeys.yourMedicalReportRequestSubmittedSuccessfully.tr(context: context)), callBackFunc: () async{ + medicalFileViewModel.setIsMedicalReportsDataNeedsReloading(true); medicalFileViewModel.setIsPatientMedicalReportsLoading(true); medicalFileViewModel.onMedicalReportTabChange(0); medicalFileViewModel.getPatientMedicalReportList(); From 133d27155b38837765c8244a68ec67c423169339 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 6 Oct 2026 10:26:45 +0300 Subject: [PATCH 13/17] updates --- assets/langs/en-US.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index ceaf6579..4cf2827c 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -2047,7 +2047,7 @@ "downloadApp": "Download App", "unread": "Unread", "read": "Read", - "pendingMedicalReportRequest": "You already have a pending medical report request for this appointment. Please wait for the request to be completed before submitting a new one.", + "pendingMedicalReportRequest": "You already have a pending medical report request for this appointment. Please wait for the request to be completed before submitting a new one." } From 5b889b0b109c0cfcf701cead8ba55964b0ff662f Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Tue, 6 Oct 2026 10:32:34 +0300 Subject: [PATCH 14/17] family file card fix --- lib/presentation/my_family/widget/family_cards.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/my_family/widget/family_cards.dart b/lib/presentation/my_family/widget/family_cards.dart index f6300be3..468dd28f 100644 --- a/lib/presentation/my_family/widget/family_cards.dart +++ b/lib/presentation/my_family/widget/family_cards.dart @@ -475,7 +475,7 @@ class _FamilyCardsState extends State { ? Wrap(direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ AppCustomChipWidget( icon: AppAssets.file_icon, - richText: "${LocaleKeys.fileno.tr(context: context)}: ${profile.responseId}".toText10(isBold: true), + richText: "${profile.responseId}".toText10(isBold: true), labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), ), AppCustomChipWidget( @@ -489,7 +489,7 @@ class _FamilyCardsState extends State { : SizedBox( height: 4.h, ), - Spacer(), + SizedBox(height: 8.h), widget.isForWalletRecharge ? CustomButton( height: 40.h, From e4aba3a50470957d5335c91b1e2254f5592f69ac Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Tue, 6 Oct 2026 10:33:04 +0300 Subject: [PATCH 15/17] family file notification key changed --- lib/core/api_consts.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index d5053e3b..784704c3 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -6,7 +6,7 @@ import 'package:myfatoorah_flutter/myfatoorah_flutter.dart'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT @@ -316,7 +316,7 @@ class ApiConsts { static String googleCloudStorageENTranslationFileBaseURL = "https://storage.googleapis.com/hmg-patientapp-translations"; // ************ static values for Api **************** - static final double appVersionID = 22.1; + static final double appVersionID = 22.5; // static final double appVersionID = 50.7; static final int appChannelId = 3; @@ -328,7 +328,7 @@ class ApiConsts { static final String notificationCenterAppGUID = "SzFwWjI0TjhmSjZreDVsNmc3SDRSdWlxUG1h"; static final String notificationServiceGUID_Login = "D8BD5C76-4B50-4122-81D0-C3AA25DB08A2"; static final String notificationServiceGUID_Registration = "690D3595-A1E1-489A-B9AB-6270E83AD6ED"; - static final String notificationServiceGUID_FamilyFile = "420D7C25-9CEF-4BCB-9963-27D9ECF8484C"; + static final String notificationServiceGUID_FamilyFile = "318285E4-6221-4A66-8FAF-2265D2B4B24C"; } // PACKAGES and OFFERS From 5d4031e02dd8921230b90ebc3c3f4b0521c7da36 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 6 Oct 2026 11:33:39 +0300 Subject: [PATCH 16/17] Reschedule logic updated --- .../book_appointments/book_appointments_repo.dart | 6 +++--- .../book_appointments_view_model.dart | 10 +++++----- lib/features/my_appointments/my_appointments_repo.dart | 6 +++--- .../my_appointments/my_appointments_view_model.dart | 4 ++-- .../appointments/appointment_details_page.dart | 1 + 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart index a705d84a..caf97cb8 100644 --- a/lib/features/book_appointments/book_appointments_repo.dart +++ b/lib/features/book_appointments/book_appointments_repo.dart @@ -37,7 +37,7 @@ abstract class BookAppointmentsRepo { Future>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}); - Future>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}); + Future>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, bool isReschedule = false}); Future>> insertSpecificAppointment( {required int docID, @@ -483,12 +483,12 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo { } @override - Future> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}) async { + Future> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, bool isReschedule = false}) async { Map requestBody = { "AppointmentID": patientAppointmentHistoryResponseModel.appointmentNo, "ClinicID": patientAppointmentHistoryResponseModel.clinicID, "ProjectID": patientAppointmentHistoryResponseModel.projectID, - "CancelToReschadual": false, + "CancelToReschadual": isReschedule, "EndTime": patientAppointmentHistoryResponseModel.endTime, "StartTime": patientAppointmentHistoryResponseModel.startTime, "DoctorID": patientAppointmentHistoryResponseModel.doctorID, diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index a4530eee..5865a829 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -948,8 +948,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } - Future cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); + Future cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, bool isReschedule = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, isReschedule: isReschedule); result.fold( (failure) async => await errorHandlerService.handleError(failure: failure), @@ -1011,7 +1011,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'], ); - await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { + await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, isReschedule: true).then((val) async { // LoaderBottomSheet.hideLoader(); Future.delayed(Duration(milliseconds: 50)).then((value) async {}); // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr()); @@ -1069,7 +1069,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { // loadingText: LocaleKeys.reschedulingAppo.tr(context: navigationService.navigatorKey.currentContext!), loadingText: LocaleKeys.loadingText.tr(context: navigationService.navigatorKey.currentContext!), ); - await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { + await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, isReschedule: true).then((val) async { LoaderBottomSheet.hideLoader(); Future.delayed(Duration(milliseconds: 50)).then((value) async {}); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr()); @@ -1174,7 +1174,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { isCloseButtonVisible: false, isDismissible: false, isFullScreen: false); - await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { + await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, isReschedule: true).then((val) async { navigationService.pop(); Future.delayed(Duration(milliseconds: 50)).then((value) async {}); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr()); diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index 87fb2ec2..ef3cd283 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -44,7 +44,7 @@ abstract class MyAppointmentsRepo { Future>> generateAppointmentQR({required int clinicID, required int projectID, required String appointmentNo, required int isFollowUp}); - Future>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}); + Future>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, bool isReschedule = false}); Future>> confirmAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}); @@ -341,12 +341,12 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { } @override - Future> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}) async { + Future> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, bool isReschedule = false}) async { Map requestBody = { "AppointmentID": patientAppointmentHistoryResponseModel.appointmentNo, "ClinicID": patientAppointmentHistoryResponseModel.clinicID, "ProjectID": patientAppointmentHistoryResponseModel.projectID, - "CancelToReschadual": false, + "CancelToReschadual": isReschedule, "EndTime": patientAppointmentHistoryResponseModel.endTime, "StartTime": patientAppointmentHistoryResponseModel.startTime, "DoctorID": patientAppointmentHistoryResponseModel.doctorID, diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index b2b990db..f040fd82 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -639,8 +639,8 @@ class MyAppointmentsViewModel extends ChangeNotifier { ); } - Future cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await myAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); + Future cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, bool isReschedule = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, isReschedule: isReschedule); result.fold( // (failure) async => await errorHandlerService.handleError(failure: failure), diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 7d1d3f91..874c97c6 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -338,6 +338,7 @@ class _AppointmentDetailsPageState extends State { myAppointmentsViewModel.updateListWRTTab(0); await myAppointmentsViewModel.cancelAppointment( patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, + isReschedule: false, onSuccess: (apiResponse) async { LoaderBottomSheet.hideLoader(); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); From b68bf1d3219c5b090f8df658a24dd928dbce0ba9 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 6 Oct 2026 15:40:18 +0300 Subject: [PATCH 17/17] Apple Pay payfort response API Added --- lib/core/api/api_client.dart | 4 +- lib/features/payfort/payfort_view_model.dart | 23 +++++ .../appointment_payment_page.dart | 85 +++++++++++++++++++ .../immediate_livecare_payment_page.dart | 2 + .../waiting_appointment_payment_page.dart | 2 + .../wallet_payment_confirm_page.dart | 2 + .../medical_file/medical_file_page.dart | 2 +- .../ancillary_order_payment_page.dart | 2 + lib/splashPage.dart | 2 +- 9 files changed, 120 insertions(+), 4 deletions(-) diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 00eebb34..0bc0211a 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -209,8 +209,8 @@ class ApiClientImp implements ApiClient { body['TokenID'] = "@dm!n"; } - body['TokenID'] = "@dm!n"; - // body['PatientID'] = 7000792; + // body['TokenID'] = "@dm!n"; + // body['PatientID'] = 1009156; // body['PatientID'] = 53320; // body['PatientTypeID'] = 1; // body['PatientOutSA'] = 0; diff --git a/lib/features/payfort/payfort_view_model.dart b/lib/features/payfort/payfort_view_model.dart index c4f96857..7b6b8b7d 100644 --- a/lib/features/payfort/payfort_view_model.dart +++ b/lib/features/payfort/payfort_view_model.dart @@ -578,4 +578,27 @@ class PayfortViewModel extends ChangeNotifier { ); } + Future addApplePayPayfortResponse(dynamic paymentMap) async { + PayfortResponseInsertModel payfortResponseInsertModel = PayfortResponseInsertModel(); + + payfortResponseInsertModel.fortId = paymentMap['fort_id'] ?? ""; + payfortResponseInsertModel.commandType = paymentMap['command'] ?? ""; + payfortResponseInsertModel.amount = num.tryParse(paymentMap['amount'] ?? "0") ?? 0; + payfortResponseInsertModel.sadadOLP = ""; + payfortResponseInsertModel.paymentOption = paymentMap['payment_option'] ?? ""; + payfortResponseInsertModel.customerIP = paymentMap['customer_ip'] ?? ""; + payfortResponseInsertModel.eCI = paymentMap['eci'] ?? ""; + payfortResponseInsertModel.responseMessage = paymentMap['response_message'] ?? ""; + payfortResponseInsertModel.cardNumber = paymentMap['card_number'] ?? ""; + payfortResponseInsertModel.cardBin = paymentMap['card_bin'] ?? ""; + payfortResponseInsertModel.installmentsNumber = int.tryParse(paymentMap['installments_number'] ?? "0") ?? 0; + payfortResponseInsertModel.status = paymentMap['status'] ?? ""; + payfortResponseInsertModel.merchantRef = paymentMap['merchant_reference'] ?? ""; + payfortResponseInsertModel.isRefund = false; + payfortResponseInsertModel.remmeberMe = true; + payfortResponseInsertModel.reconciliationReference = ""; + + payfortResponseInsert(payfortResponseInsertModel: payfortResponseInsertModel); + } + } diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index 5b7e4c80..bef2df93 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -1283,6 +1283,7 @@ class _AppointmentPaymentPageState extends State { currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", onFailed: (failureResult) async { log("failureResult: ${failureResult.message.toString()}"); + payfortViewModel.addApplePayPayfortResponse(failureResult.response); LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( context, @@ -1293,10 +1294,94 @@ class _AppointmentPaymentPageState extends State { ); }, onSucceeded: (successResult) async { + payfortViewModel.addApplePayPayfortResponse(successResult); LoaderBottomSheet.hideLoader(); log("successResult: ${successResult.responseMessage.toString()}"); selectedPaymentMethod = successResult.paymentOption ?? "VISA"; checkPaymentStatus(); + + // 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: successResult.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: successResult.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); + // }); + // }); + // } + // }); + // }, + // onError: (err) { + // LoaderBottomSheet.hideLoader(); + // showCommonBottomSheetWithoutHeight( + // context, + // child: Utils.getErrorWidget(loadingText: err.toString()), + // callBackFunc: () {}, + // isFullScreen: false, + // isCloseButtonVisible: true, + // ); + // }); }, // projectId: appo.projectID, // serviceTypeEnum: ServiceTypeEnum.appointmentPayment, 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 9d30f976..b205063c 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart @@ -1077,6 +1077,7 @@ class _ImmediateLiveCarePaymentPageState extends State { currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", onFailed: (failureResult) async { log("failureResult: ${failureResult.message.toString()}"); + payfortViewModel.addApplePayPayfortResponse(failureResult.response); showCommonBottomSheetWithoutHeight( context, child: Utils.getErrorWidget(loadingText: failureResult.message.toString()), @@ -678,6 +679,7 @@ class _WalletPaymentConfirmPageState extends State { }, onSucceeded: (successResult) async { log("successResult: ${successResult.responseMessage.toString()}"); + payfortViewModel.addApplePayPayfortResponse(successResult); selectedPaymentMethod = successResult.paymentOption ?? "VISA"; checkPaymentStatus(); }, diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 629ff15c..add423d8 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -280,7 +280,7 @@ class _MedicalFilePageState extends State { AppCustomChipWidget( icon: AppAssets.blood_icon, labelText: appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup, - iconColor: AppColors.primaryRedColor, + iconColor: AppColors.primaryRedColorBase, labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w), padding: EdgeInsets.zero, ), diff --git a/lib/presentation/todo_section/ancillary_order_payment_page.dart b/lib/presentation/todo_section/ancillary_order_payment_page.dart index 365dff2b..e6665037 100644 --- a/lib/presentation/todo_section/ancillary_order_payment_page.dart +++ b/lib/presentation/todo_section/ancillary_order_payment_page.dart @@ -819,6 +819,7 @@ class _AncillaryOrderPaymentPageState extends State { applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest, currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", onFailed: (failureResult) async { + payfortViewModel.addApplePayPayfortResponse(failureResult.response); log("failureResult: ${failureResult.message.toString()}"); LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( @@ -830,6 +831,7 @@ class _AncillaryOrderPaymentPageState extends State { ); }, onSucceeded: (successResult) async { + payfortViewModel.addApplePayPayfortResponse(successResult); LoaderBottomSheet.hideLoader(); log("successResult: ${successResult.responseMessage.toString()}"); selectedPaymentMethod = successResult.paymentOption ?? "VISA"; diff --git a/lib/splashPage.dart b/lib/splashPage.dart index 8ebe655e..88c247b6 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -93,7 +93,7 @@ class _SplashScreenState extends State { if (isAppOpenedFromCall) { navigateToTeleConsult(); } else { - if (Utils.havePrivilege(124)) { + if (!kDebugMode && Utils.havePrivilege(124)) { Navigator.of(getIt.get().navigatorKey.currentContext!).pushAndRemoveUntil(CustomPageRoute(page: AppClosedPage()), (r) => false); } else { if (await Utils.getBoolFromPrefs(CacheConst.firstLaunch)) {