Merge remote-tracking branch 'origin/Main_App_Replacement' into Main_App_Replacement

lab-order-status
taha 3 weeks ago
commit f021eb8bc9

@ -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.** { *; }

@ -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 ->

@ -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:MaxMetaspaceSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

@ -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

@ -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

@ -0,0 +1,10 @@
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="120" height="120" rx="60" fill="#ED1C2B" fill-opacity="0.0784314"/>
<rect x="27" y="45" width="18" height="30" rx="3" fill="white"/>
<rect x="27" y="45" width="18" height="30" rx="3" stroke="#ED1C2B" stroke-width="2"/>
<rect x="30" y="52" width="12" height="16" rx="2" fill="#ED1C2B" fill-opacity="0.101961"/>
<path d="M55.333 59.9992H64.6674M60.0002 64.6664L64.6674 59.9992L60.0002 55.332" stroke="#ED1C2B" stroke-width="2" stroke-linecap="round"/>
<rect x="75" y="45" width="18" height="30" rx="3" fill="white"/>
<rect x="75" y="45" width="18" height="30" rx="3" stroke="#ED1C2B" stroke-width="2"/>
<rect x="78" y="52" width="12" height="16" rx="2" fill="#ED1C2B" fill-opacity="0.101961"/>
</svg>

After

Width:  |  Height:  |  Size: 812 B

@ -2063,5 +2063,12 @@
"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": "حمل التطبيق",
"unread": "غير مقروء",
"read": "مقروءة",
"pendingMedicalReportRequest": "لديك بالفعل طلب معلّق للحصول على تقرير طبي خاص بهذا الموعد. يرجى الانتظار حتى اكتمال الطلب قبل تقديم طلب جديد."
}

@ -2053,7 +2053,14 @@
"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",
"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."
}

@ -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<AppClosedPage> createState() => _AppClosedPageState();
}
class _AppClosedPageState extends State<AppClosedPage> {
@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,
),
],
),
),
),
);
}
}

@ -210,7 +210,7 @@ class ApiClientImp implements ApiClient {
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 7000792;
// body['PatientID'] = 1009156;
// body['PatientID'] = 53320;
// body['PatientTypeID'] = 1;
// body['PatientOutSA'] = 0;

@ -317,13 +317,19 @@ class ApiConsts {
// ************ static values for Api ****************
// static final double appVersionID = 21.9;
static final double appVersionID = 21.3;
static final double appVersionID = 21.5;
// static final double appVersionID = 50.7;
static final int appChannelId = 3;
static final String appIpAddress = "10.20.10.20";
static final String appGeneralId = "Cs2020@2016\$2958";
static final String sessionID = 'TMRhVmkGhOsvamErw';
// Notification Center GUIDs - Configure these values as per your notification center setup
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 = "318285E4-6221-4A66-8FAF-2265D2B4B24C";
}
// PACKAGES and OFFERS
@ -387,8 +393,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";

@ -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';

@ -75,6 +75,12 @@ class RequestUtils {
}
request.isRegister = false;
}
request.notificationCenterAppGUID = ApiConsts.notificationCenterAppGUID;
request.notificationCenterAppServiceGUID = isForRegister
? ApiConsts.notificationServiceGUID_Registration
: ApiConsts.notificationServiceGUID_Login;
return request;
}
@ -148,6 +154,12 @@ class RequestUtils {
}
request.deviceTypeID = request.searchType;
request.notificationCenterAppGUID = ApiConsts.notificationCenterAppGUID;
request.notificationCenterAppServiceGUID = (registeredData != null && registeredData.isRegister == true)
? ApiConsts.notificationServiceGUID_Registration
: ApiConsts.notificationServiceGUID_Login;
return request;
}
@ -220,6 +232,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;
}

@ -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;

@ -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';
@ -1522,7 +1523,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();
@ -1831,11 +1834,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.setPrivilegeModelList([]);
// _appState.setVidaPlusProjectList([]);
// _appState.setHMCProjectList([]);
// _appState.setProjectsDetailList([]);
_appState.clearUserSwitchPayloads();
await clearDefaultInputValues();
_navigationService.pushAndReplace(AppRoutes.landingScreen);

@ -31,6 +31,8 @@ class SendActivationRequest {
int? familyRegionID;
bool? isPatientExcluded;
String? turnstileToken;
String? notificationCenterAppGUID;
String? notificationCenterAppServiceGUID;
SendActivationRequest(
{this.patientMobileNumber,
@ -64,7 +66,10 @@ class SendActivationRequest {
this.status,
this.familyRegionID,
this.isPatientExcluded,
this.turnstileToken});
this.turnstileToken,
this.notificationCenterAppGUID,
this.notificationCenterAppServiceGUID
});
SendActivationRequest.fromJson(Map<String, dynamic> json) {
patientMobileNumber = json['PatientMobileNumber'];
@ -99,6 +104,8 @@ class SendActivationRequest {
familyRegionID = json['FamilyRegionID'];
isPatientExcluded = json['IsPatientExcluded'];
turnstileToken = json['TurnstileToken'];
notificationCenterAppGUID = json['NotificationCenterAppGUID'];
notificationCenterAppServiceGUID = json['NotificationCenterAppSeviceGUID'];
}
Map<String, dynamic> toJson() {
@ -141,6 +148,8 @@ class SendActivationRequest {
if (turnstileToken != null) {
data['TurnstileToken'] = turnstileToken;
}
data['NotificationCenterAppGUID'] = notificationCenterAppGUID;
data['NotificationCenterAppSeviceGUID'] = notificationCenterAppServiceGUID;
return data;
}
}

@ -37,7 +37,7 @@ abstract class BookAppointmentsRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare,
{bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
Future<Either<Failure, GenericApiModel<dynamic>>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, bool isReschedule = false});
Future<Either<Failure, GenericApiModel<dynamic>>> insertSpecificAppointment(
{required int docID,
@ -483,12 +483,12 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
}
@override
Future<Either<Failure, GenericApiModel>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}) async {
Future<Either<Failure, GenericApiModel>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, bool isReschedule = false}) async {
Map<String, dynamic> requestBody = {
"AppointmentID": patientAppointmentHistoryResponseModel.appointmentNo,
"ClinicID": patientAppointmentHistoryResponseModel.clinicID,
"ProjectID": patientAppointmentHistoryResponseModel.projectID,
"CancelToReschadual": false,
"CancelToReschadual": isReschedule,
"EndTime": patientAppointmentHistoryResponseModel.endTime,
"StartTime": patientAppointmentHistoryResponseModel.startTime,
"DoctorID": patientAppointmentHistoryResponseModel.doctorID,

@ -948,8 +948,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
);
}
Future<void> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel);
Future<void> 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());

@ -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;
}
}

@ -44,7 +44,7 @@ abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> generateAppointmentQR({required int clinicID, required int projectID, required String appointmentNo, required int isFollowUp});
Future<Either<Failure, GenericApiModel<dynamic>>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
Future<Either<Failure, GenericApiModel<dynamic>>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, bool isReschedule = false});
Future<Either<Failure, GenericApiModel<dynamic>>> confirmAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
@ -341,12 +341,12 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
}
@override
Future<Either<Failure, GenericApiModel>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}) async {
Future<Either<Failure, GenericApiModel>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, bool isReschedule = false}) async {
Map<String, dynamic> requestBody = {
"AppointmentID": patientAppointmentHistoryResponseModel.appointmentNo,
"ClinicID": patientAppointmentHistoryResponseModel.clinicID,
"ProjectID": patientAppointmentHistoryResponseModel.projectID,
"CancelToReschadual": false,
"CancelToReschadual": isReschedule,
"EndTime": patientAppointmentHistoryResponseModel.endTime,
"StartTime": patientAppointmentHistoryResponseModel.startTime,
"DoctorID": patientAppointmentHistoryResponseModel.doctorID,

@ -639,8 +639,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
);
}
Future<void> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel);
Future<void> 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),

@ -13,11 +13,16 @@ class NotificationsViewModel extends ChangeNotifier {
ErrorHandlerService errorHandlerService;
List<NotificationResponseModel> notificationsList = [];
List<NotificationResponseModel> readdNotificationsList = [];
List<NotificationResponseModel> unreadNotificationsList = [];
List<NotificationResponseModel> 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());
}
}
},

@ -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-07T00: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,

@ -578,4 +578,27 @@ class PayfortViewModel extends ChangeNotifier {
);
}
Future<void> 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);
}
}

@ -2032,5 +2032,12 @@ 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';
static const unread = 'unread';
static const read = 'read';
static const pendingMedicalReportRequest = 'pendingMedicalReportRequest';
}

@ -338,6 +338,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
myAppointmentsViewModel.updateListWRTTab(0);
await myAppointmentsViewModel.cancelAppointment(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
isReschedule: false,
onSuccess: (apiResponse) async {
LoaderBottomSheet.hideLoader();
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);

@ -748,7 +748,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(myAppointmentsVM.patientAppointmentShareResponseModel!.isCash ?? true)
// (myAppointmentsVM.patientAppointmentShareResponseModel!.isCash ?? true)
(true)
? Container(
height: 50.h,
decoration: ShapeDecoration(
@ -1282,6 +1283,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
onFailed: (failureResult) async {
log("failureResult: ${failureResult.message.toString()}");
payfortViewModel.addApplePayPayfortResponse(failureResult.response);
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
@ -1292,10 +1294,94 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
);
},
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<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
// getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
// getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
//
// LoaderBottomSheet.hideLoader();
// Navigator.pushAndRemoveUntil(
// context,
// CustomPageRoute(
// page: LandingNavigation(),
// ),
// (r) => false);
// });
// },
// onError: (error) {});
// } else {
// await myAppointmentsViewModel.generateAppointmentQR(
// clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
// projectID: widget.patientAppointmentHistoryResponseModel.projectID,
// appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
// isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!,
// onSuccess: (apiResponse) {
// Future.delayed(Duration(milliseconds: 500), () {
// myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
// myAppointmentsViewModel.initAppointmentsViewModel();
// myAppointmentsViewModel.getPatientAppointments(true, false);
//
// getIt.get<HabibWalletViewModel>().setIsWalletAmountToBeLoaded(true);
// getIt.get<HabibWalletViewModel>().initHabibWalletProvider();
// getIt.get<HabibWalletViewModel>().getPatientBalanceAmount();
//
// LoaderBottomSheet.hideLoader();
// Navigator.pushAndRemoveUntil(
// context,
// CustomPageRoute(
// page: LandingNavigation(),
// ),
// (r) => false);
// });
// });
// }
// });
// },
// onError: (err) {
// LoaderBottomSheet.hideLoader();
// showCommonBottomSheetWithoutHeight(
// context,
// child: Utils.getErrorWidget(loadingText: err.toString()),
// callBackFunc: () {},
// isFullScreen: false,
// isCloseButtonVisible: true,
// );
// });
},
// projectId: appo.projectID,
// serviceTypeEnum: ServiceTypeEnum.appointmentPayment,

@ -448,7 +448,17 @@ class _AppointmentCardState extends State<AppointmentCard> {
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 {

@ -759,8 +759,8 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
.onPress(() async {
// payfortVM.setIsApplePayConfigurationLoading(true);
if (Utils.havePrivilege(103)) {
// startApplePay();
myFatoorahViewModel.executeApplePayPayment();
startApplePay();
// myFatoorahViewModel.executeApplePayPayment();
} else {
openPaymentURL("ApplePay");
}
@ -1077,6 +1077,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
onFailed: (failureResult) async {
log("failureResult: ${failureResult.message.toString()}");
payfortViewModel.addApplePayPayfortResponse(failureResult.response);
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
@ -1087,6 +1088,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
);
},
onSucceeded: (successResult) async {
payfortViewModel.addApplePayPayfortResponse(successResult);
LoaderBottomSheet.hideLoader();
log("successResult: ${successResult.responseMessage.toString()}");
selectedPaymentMethod = successResult.paymentOption ?? "VISA";

@ -904,6 +904,7 @@ class _WaitingAppointmentPaymentPageState extends State<WaitingAppointmentPaymen
applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest,
currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
onFailed: (failureResult) async {
payfortViewModel.addApplePayPayfortResponse(failureResult.response);
log("failureResult: ${failureResult.message.toString()}");
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight(
@ -915,6 +916,7 @@ class _WaitingAppointmentPaymentPageState extends State<WaitingAppointmentPaymen
);
},
onSucceeded: (successResult) async {
payfortViewModel.addApplePayPayfortResponse(successResult);
log("successResult: ${successResult.responseMessage.toString()}");
selectedPaymentMethod = successResult.paymentOption ?? "VISA";
checkPaymentStatus();

@ -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';
@ -668,6 +667,7 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
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 +678,7 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
},
onSucceeded: (successResult) async {
log("successResult: ${successResult.responseMessage.toString()}");
payfortViewModel.addApplePayPayfortResponse(successResult);
selectedPaymentMethod = successResult.paymentOption ?? "VISA";
checkPaymentStatus();
},
@ -830,28 +831,4 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
"",
"3");
}
Future<void> runSafePayment(Map<String, dynamic> requestParam, {String sdkToken = ""}) async {
dynamic result;
try {
// Force a 100ms microtask buffer to isolate the UI rendering channel
await Future.delayed(const Duration(milliseconds: 350));
result = await FlutterAmazonpaymentservices.normalPay(requestParam, ApiConsts.payFortEnvironmentType, isShowResponsePage: true);
print("Payment Success Data: ${result.toString()}");
} on PlatformException catch (e) {
// If the error is the MethodChannel duplicate reply bug, handle it safely
if (e.message != null && e.message!.contains("Reply already submitted")) {
print("Swallowed native channel race-condition crash safely.");
return;
}
// Process real payment rejection errors here
print("Actual Payment Failure: ${e.details.toString()}");
} catch (error) {
print("Generic Error: $error");
}
}
}

@ -72,7 +72,7 @@ class _ServicesPageState extends State<ServicesPage> {
LocaleKeys.emergencyServices.tr(),
"",
AppAssets.emergency_services_icon,
bgColor: AppColors.primaryRedColor,
bgColor: AppColors.primaryRedColorBase,
true,
route: null, onTap: () async {
// if (getIt.get<AppState>().isAuthenticated) {

@ -437,6 +437,7 @@ class _LandingPageState extends State<LandingPage> {
: SizedBox.shrink(),
]).onPress(() async {
if (appState.isAuthenticated) {
notificationsViewModel.onNotificationTabChange(0);
notificationsViewModel.setNotificationStatusID(2);
notificationsViewModel.getAllNotifications();
Navigator.of(context).push(

@ -280,7 +280,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
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,
),

@ -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),
],
),

@ -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,

@ -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),
],
),

@ -50,42 +50,7 @@ class _MedicalReportsPageState extends State<MedicalReportsPage> {
child: Consumer<MedicalFileViewModel>(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(
@ -404,6 +369,7 @@ class _MedicalReportsPageState extends State<MedicalReportsPage> {
showCommonBottomSheetWithoutHeight(context,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.yourMedicalReportRequestSubmittedSuccessfully.tr(context: context)),
callBackFunc: () async{
medicalFileViewModel.setIsMedicalReportsDataNeedsReloading(true);
medicalFileViewModel.setIsPatientMedicalReportsLoading(true);
medicalFileViewModel.onMedicalReportTabChange(0);
medicalFileViewModel.getPatientMedicalReportList();

@ -475,7 +475,7 @@ class _FamilyCardsState extends State<FamilyCards> {
? 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<FamilyCards> {
: SizedBox(
height: 4.h,
),
Spacer(),
SizedBox(height: 8.h),
widget.isForWalletRecharge
? CustomButton(
height: 40.h,

@ -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<NotificationsListPage> {
child: Consumer<NotificationsViewModel>(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(

@ -819,6 +819,7 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
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<AncillaryOrderPaymentPage> {
);
},
onSucceeded: (successResult) async {
payfortViewModel.addApplePayPayfortResponse(successResult);
LoaderBottomSheet.hideLoader();
log("successResult: ${successResult.responseMessage.toString()}");
selectedPaymentMethod = successResult.paymentOption ?? "VISA";

@ -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;

@ -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),

@ -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';
@ -121,24 +123,24 @@ class _SplashScreenState extends State<SplashPage> {
// Set initial app context tags
try {
final appState = getIt.get<AppState>();
// 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)
@ -149,10 +151,9 @@ class _SplashScreenState extends State<SplashPage> {
} else {
Clarity.setCustomTag('auth_status', 'guest');
}
// Send initialization event
Clarity.sendCustomEvent('app_initialized');
} catch (e) {
debugPrint('Failed to set Clarity user context: $e');
}
@ -165,8 +166,7 @@ class _SplashScreenState extends State<SplashPage> {
// GetIt.instance<CacheService>().remove(key: CacheConst.isAppOpenedFromCall);
Utils.removeFromPrefs(CacheConst.isAppOpenedFromCall);
Navigator.of(GetIt.instance<NavigationService>().navigatorKey.currentContext!)
.pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation())));
Navigator.of(GetIt.instance<NavigationService>().navigatorKey.currentContext!).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation())));
Navigator.pushReplacementNamed(
// context,
GetIt.instance<NavigationService>().navigatorKey.currentContext!,
@ -386,8 +386,6 @@ class _SplashScreenState extends State<SplashPage> {
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);
}
}

@ -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 {
'<input type="hidden" name="ProjID" value="PROJECT_ID_VALUE">' +
'<input type="hidden" name="Order_Desc" value="ORDER_DESCRIPTION_VALUE">' +
'<input type="hidden" name="OrderID" value="ORDER_ID_VALUE">' +
'<input type="hidden" name="AppointmentDate" value="APPOINTMENT_DATE_VALUE">' +
'<input type="hidden" name="AppointmentNo" value="APPOINTMENT_NO_VALUE">' +
'<input type="hidden" name="DoctorID" value="DOCTOR_ID_VALUE">' +
'<input type="hidden" name="ClinicID" value="CLINIC_ID_VALUE">' +
'<input type="hidden" name="PaymentOption" value="PAYMENT_OPTION_VALUE">' +
'<input type="hidden" name="Email" value="EMAIL_VALUE">' +
'<input type="hidden" name="ServID" value="SERV_ID" >' +

@ -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

Loading…
Cancel
Save