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

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

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

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

@ -2054,5 +2054,8 @@
"importantNotice": "تنبيه هام",
"appDecommissionedText": "تم إيقاف هذا الإصدار من تطبيق الحبيب الطبي.\nللاستمرار في الاستفادة من الخدمات الصحية والسجلات الطبية، يرجى تحميل التطبيق الجديد.",
"appDecommissionedSubText": "نعتذر بشدة عن الإزعاج الناتج عن هذا الانتقال.",
"downloadApp": "حمل التطبيق"
"downloadApp": "حمل التطبيق",
"unread": "غير مقروء",
"read": "مقروءة",
"pendingMedicalReportRequest": "لديك بالفعل طلب معلّق للحصول على تقرير طبي خاص بهذا الموعد. يرجى الانتظار حتى اكتمال الطلب قبل تقديم طلب جديد."
}

@ -2044,7 +2044,10 @@
"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",
"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."
}

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

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

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

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

@ -2036,5 +2036,8 @@ abstract class LocaleKeys {
static const appDecommissionedText = 'appDecommissionedText';
static const appDecommissionedSubText = 'appDecommissionedSubText';
static const downloadApp = 'downloadApp';
static const unread = 'unread';
static const read = 'read';
static const pendingMedicalReportRequest = 'pendingMedicalReportRequest';
}

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

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

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

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

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

@ -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" >' +

Loading…
Cancel
Save