diff --git a/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinNavigator.kt b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinNavigator.kt
index b822d676..70889d37 100644
--- a/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinNavigator.kt
+++ b/android/app/src/main/kotlin/com/ejada/hmg/penguin/PenguinNavigator.kt
@@ -21,7 +21,7 @@ class PenguinNavigator() {
val postToken = PostToken(clientID, clientKey)
getToken(mContext, postToken, object : RefIdDelegate {
override fun onRefByIDSuccess(PoiId: String?) {
- Log.e("navigateTo", "PoiId is+++++++ $PoiId")
+ Log.e("navigateTo", "PoiId is+++++++ $refID")
PlugAndPlaySDK.navigateTo(mContext, refID, object : RefIdDelegate {
override fun onRefByIDSuccess(PoiId: String?) {
diff --git a/assets/images/svg/back_top_nav_icon.svg b/assets/images/svg/back_top_nav_icon.svg
new file mode 100644
index 00000000..62bf96b5
--- /dev/null
+++ b/assets/images/svg/back_top_nav_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/forward_top_nav_icon.svg b/assets/images/svg/forward_top_nav_icon.svg
new file mode 100644
index 00000000..089161c8
--- /dev/null
+++ b/assets/images/svg/forward_top_nav_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/ios/Runner/Penguin/PenguinNavigator.swift b/ios/Runner/Penguin/PenguinNavigator.swift
index 31cf6262..5b9934bd 100644
--- a/ios/Runner/Penguin/PenguinNavigator.swift
+++ b/ios/Runner/Penguin/PenguinNavigator.swift
@@ -14,7 +14,7 @@ class PenguinNavigator {
}
func navigateToPOI( referenceId:String,completion: @escaping (Bool, String?) -> Void) {
- PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey, showProgress: true) { [weak self] token, error in
+ PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey, showProgress: false) { [weak self] token, error in
if let error = error {
let errorMessage = "Token error while getting the for Navigate to method"
diff --git a/ios/Runner/Penguin/PenguinView.swift b/ios/Runner/Penguin/PenguinView.swift
index d5303e2e..508fb74a 100644
--- a/ios/Runner/Penguin/PenguinView.swift
+++ b/ios/Runner/Penguin/PenguinView.swift
@@ -312,6 +312,8 @@ class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitia
print("====== after eventsDelegate onPenNavSuccess =========")
+
+ PenNavUIManager.shared.navigate(to: "108")
@@ -385,21 +387,21 @@ class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitia
- self?.handleNavigation(clinicID: clinicID, token: token) { success, errorMessage in
-
- if success {
-
- print("Navigation successful")
-
- } else {
-
- print("Navigation failed: \(errorMessage ?? "Unknown error")")
-
- }
-
-
-
- }
+// self?.handleNavigation(clinicID: clinicID, token: token) { success, errorMessage in
+//
+// if success {
+//
+// print("Navigation successful")
+//
+// } else {
+//
+// print("Navigation failed: \(errorMessage ?? "Unknown error")")
+//
+// }
+//
+//
+//
+// }
diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart
index 1846d1e0..edce2368 100644
--- a/lib/core/api_consts.dart
+++ b/lib/core/api_consts.dart
@@ -398,6 +398,9 @@ var GET_COVID_DRIVETHRU_PROCEDURES_LIST = 'Services/Doctors.svc/REST/COVID19_Get
var GET_PATIENT_LAST_RECORD = 'Services/Patients.svc/REST/Med_GetPatientLastRecord';
var INSERT_PATIENT_HEALTH_DATA = 'Services/Patients.svc/REST/Med_InsertTransactions';
+///Push Notifications
+var GET_ALL_NOTIFICATIONS = 'Services/MobileNotifications.svc/REST/PushNotification_GetAllNotifications';
+
///My Trackers
var GET_DIABETIC_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetDiabeticResultAverage';
var GET_DIABTEC_RESULT = 'Services/Patients.svc/REST/Patient_GetDiabtecResults';
@@ -672,10 +675,12 @@ const FAMILY_FILES = 'Services/Authentication.svc/REST/GetAllSharedRecordsByStat
var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatbot_SendMedicationInstructionByWhatsApp';
+const DASHBOARD = 'Services/Patients.svc/REST/PatientDashboard';
+
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
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index a581e786..3555e62d 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -218,11 +218,13 @@ class AppAssets {
static const String activity = '$svgBasePath/activity.svg';
static const String age = '$svgBasePath/age_icon.svg';
static const String gender = '$svgBasePath/gender_icon.svg';
-
static const String trade_down_yellow = '$svgBasePath/trade_down_yellow.svg';
static const String trade_down_red = '$svgBasePath/trade_down_red.svg';
static const String pharmacy_icon = '$svgBasePath/phramacy_icon.svg';
+ static const String forward_top_nav_icon = '$svgBasePath/forward_top_nav_icon.svg';
+ static const String back_top_nav_icon = '$svgBasePath/back_top_nav_icon.svg';
+
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';
static const String bookAppoBottom = '$svgBasePath/book_appo_bottom.svg';
diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart
index ebf0a844..43e33c4c 100644
--- a/lib/core/dependencies.dart
+++ b/lib/core/dependencies.dart
@@ -38,6 +38,8 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_rep
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_repo.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_view_model.dart';
+import 'package:hmg_patient_app_new/features/notifications/notifications_repo.dart';
+import 'package:hmg_patient_app_new/features/notifications/notifications_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart';
@@ -144,6 +146,7 @@ class AppDependencies {
getIt.registerLazySingleton(() => WaterMonitorRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => MyInvoicesRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => MonthlyReportRepoImp(loggerService: getIt(), apiClient: getIt()));
+ getIt.registerLazySingleton(() => NotificationsRepoImp(loggerService: getIt(), apiClient: getIt()));
// ViewModels
// Global/shared VMs → LazySingleton
@@ -281,5 +284,7 @@ class AppDependencies {
getIt.registerLazySingleton(() => MyInvoicesViewModel(myInvoicesRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton(() => MonthlyReportViewModel(errorHandlerService: getIt(), monthlyReportRepo: getIt()));
+
+ getIt.registerLazySingleton(() => NotificationsViewModel(notificationsRepo: getIt(), errorHandlerService: getIt()));
}
}
diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart
index 03ff6b59..e8fa6508 100644
--- a/lib/core/utils/utils.dart
+++ b/lib/core/utils/utils.dart
@@ -43,7 +43,7 @@ class Utils {
{
"Desciption": "Sahafa Hospital",
"DesciptionN": "مستشفى الصحافة",
- "ID": 1,
+ "ID": 1, // Campus ID
"LegalName": "Sahafa Hospital",
"LegalNameN": "مستشفى الصحافة",
"Name": "Sahafa Hospital",
@@ -62,12 +62,12 @@ class Utils {
"UsingInDoctorApp": false
},{
"Desciption": "Jeddah Hospital",
- "DesciptionN": "مستشفى الصحافة",
- "ID": 3,
+ "DesciptionN": "مستشفى جدة",
+ "ID": 3, // Campus ID
"LegalName": "Jeddah Hospital",
- "LegalNameN": "مستشفى الصحافة",
+ "LegalNameN": "مستشفى جدة",
"Name": "Jeddah Hospital",
- "NameN": "مستشفى الصحافة",
+ "NameN": "مستشفى جدة",
"PhoneNumber": "+966115222222",
"SetupID": "013311",
"DistanceInKilometers": 0,
diff --git a/lib/features/hospital/hospital_selection_view_model.dart b/lib/features/hospital/hospital_selection_view_model.dart
index dd9531f2..59c881ce 100644
--- a/lib/features/hospital/hospital_selection_view_model.dart
+++ b/lib/features/hospital/hospital_selection_view_model.dart
@@ -79,13 +79,14 @@ class HospitalSelectionBottomSheetViewModel extends ChangeNotifier {
}
- void openPenguin(HospitalsModel hospital) {
- initPenguinSDK(hospital.iD);
+ void openPenguin(HospitalsModel hospital, {String clinicID = ""}) {
+ initPenguinSDK(hospital.iD, clinicID: clinicID);
}
- initPenguinSDK(int projectID) async {
+ initPenguinSDK(int projectID, {String clinicID = ""}) async {
NavigationClinicDetails data = NavigationClinicDetails();
data.projectId = projectID.toString();
+ data.clinicId = clinicID;
final bool permited = await AppPermission.askPenguinPermissions();
if (!permited) {
Map statuses = await [
diff --git a/lib/features/notifications/models/resp_models/notification_response_model.dart b/lib/features/notifications/models/resp_models/notification_response_model.dart
new file mode 100644
index 00000000..034e19ca
--- /dev/null
+++ b/lib/features/notifications/models/resp_models/notification_response_model.dart
@@ -0,0 +1,96 @@
+class NotificationResponseModel {
+ int? id;
+ int? recordId;
+ int? patientID;
+ bool? projectOutSA;
+ String? deviceType;
+ String? deviceToken;
+ String? message;
+ String? messageType;
+ String? messageTypeData;
+ String? videoURL;
+ bool? isQueue;
+ String? isQueueOn;
+ String? createdOn;
+ dynamic createdBy;
+ String? notificationType;
+ bool? isSent;
+ String? isSentOn;
+ bool? isRead;
+ dynamic isReadOn;
+ int? channelID;
+ int? projectID;
+
+ NotificationResponseModel(
+ {this.id,
+ this.recordId,
+ this.patientID,
+ this.projectOutSA,
+ this.deviceType,
+ this.deviceToken,
+ this.message,
+ this.messageType,
+ this.messageTypeData,
+ this.videoURL,
+ this.isQueue,
+ this.isQueueOn,
+ this.createdOn,
+ this.createdBy,
+ this.notificationType,
+ this.isSent,
+ this.isSentOn,
+ this.isRead,
+ this.isReadOn,
+ this.channelID,
+ this.projectID});
+
+ NotificationResponseModel.fromJson(Map json) {
+ id = json['Id'];
+ recordId = json['RecordId'];
+ patientID = json['PatientID'];
+ projectOutSA = json['ProjectOutSA'];
+ deviceType = json['DeviceType'];
+ deviceToken = json['DeviceToken'];
+ message = json['Message'];
+ messageType = json['MessageType'];
+ messageTypeData = json['MessageTypeData'];
+ videoURL = json['VideoURL'];
+ isQueue = json['IsQueue'];
+ isQueueOn = json['IsQueueOn'];
+ createdOn = json['CreatedOn'];
+ createdBy = json['CreatedBy'];
+ notificationType = json['NotificationType'];
+ isSent = json['IsSent'];
+ isSentOn = json['IsSentOn'];
+ isRead = json['IsRead'];
+ isReadOn = json['IsReadOn'];
+ channelID = json['ChannelID'];
+ projectID = json['ProjectID'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['Id'] = this.id;
+ data['RecordId'] = this.recordId;
+ data['PatientID'] = this.patientID;
+ data['ProjectOutSA'] = this.projectOutSA;
+ data['DeviceType'] = this.deviceType;
+ data['DeviceToken'] = this.deviceToken;
+ data['Message'] = this.message;
+ data['MessageType'] = this.messageType;
+ data['MessageTypeData'] = this.messageTypeData;
+ data['VideoURL'] = this.videoURL;
+ data['IsQueue'] = this.isQueue;
+ data['IsQueueOn'] = this.isQueueOn;
+ data['CreatedOn'] = this.createdOn;
+ data['CreatedBy'] = this.createdBy;
+ data['NotificationType'] = this.notificationType;
+ data['IsSent'] = this.isSent;
+ data['IsSentOn'] = this.isSentOn;
+ data['IsRead'] = this.isRead;
+ data['IsReadOn'] = this.isReadOn;
+ data['ChannelID'] = this.channelID;
+ data['ProjectID'] = this.projectID;
+ return data;
+ }
+}
diff --git a/lib/features/notifications/notifications_repo.dart b/lib/features/notifications/notifications_repo.dart
new file mode 100644
index 00000000..853a9792
--- /dev/null
+++ b/lib/features/notifications/notifications_repo.dart
@@ -0,0 +1,78 @@
+import 'package:dartz/dartz.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/common_models/generic_api_model.dart';
+import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
+import 'package:hmg_patient_app_new/features/notifications/models/resp_models/notification_response_model.dart';
+import 'package:hmg_patient_app_new/services/logger_service.dart';
+
+abstract class NotificationsRepo {
+ Future>>> getAllNotifications({
+ required int notificationStatusID,
+ required int pagingSize,
+ required int currentPage,
+ });
+}
+
+class NotificationsRepoImp implements NotificationsRepo {
+ final ApiClient apiClient;
+ final LoggerService loggerService;
+
+ NotificationsRepoImp({required this.loggerService, required this.apiClient});
+
+ @override
+ Future>>> getAllNotifications({
+ required int notificationStatusID,
+ required int pagingSize,
+ required int currentPage,
+ }) async {
+ Map mapDevice = {
+ "NotificationStatusID": notificationStatusID,
+ "pagingSize": pagingSize,
+ "currentPage": currentPage,
+ };
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_ALL_NOTIFICATIONS,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final list = response['List_GetAllNotificationsFromPool'];
+ if (list == null || list.isEmpty) {
+ // Return empty list if no notifications
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: [],
+ );
+ return;
+ }
+
+ final notifications = list.map((item) => NotificationResponseModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: notifications,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+}
diff --git a/lib/features/notifications/notifications_view_model.dart b/lib/features/notifications/notifications_view_model.dart
new file mode 100644
index 00000000..e15e267c
--- /dev/null
+++ b/lib/features/notifications/notifications_view_model.dart
@@ -0,0 +1,113 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/features/notifications/models/resp_models/notification_response_model.dart';
+import 'package:hmg_patient_app_new/features/notifications/notifications_repo.dart';
+import 'package:hmg_patient_app_new/services/error_handler_service.dart';
+
+class NotificationsViewModel extends ChangeNotifier {
+ bool isNotificationsLoading = false;
+ bool hasMoreNotifications = true;
+
+ NotificationsRepo notificationsRepo;
+ ErrorHandlerService errorHandlerService;
+
+ List notificationsList = [];
+
+ int currentPage = 0;
+ int pagingSize = 14;
+ int notificationStatusID = 2; // Default to status 2 (e.g., unread)
+
+ NotificationsViewModel({
+ required this.notificationsRepo,
+ required this.errorHandlerService,
+ });
+
+ initNotificationsViewModel({int? statusID}) {
+ notificationsList.clear();
+ currentPage = 0;
+ hasMoreNotifications = true;
+ isNotificationsLoading = true;
+ // if (statusID != null) {
+ // notificationStatusID = statusID;
+ // }
+ // getAllNotifications();
+ notifyListeners();
+ }
+
+ setNotificationStatusID(int statusID) {
+ notificationStatusID = statusID;
+ notificationsList.clear();
+ currentPage = 0;
+ hasMoreNotifications = true;
+ notifyListeners();
+ }
+
+ Future getAllNotifications({
+ Function(dynamic)? onSuccess,
+ Function(String)? onError,
+ }) async {
+ isNotificationsLoading = true;
+ notifyListeners();
+
+ final result = await notificationsRepo.getAllNotifications(
+ notificationStatusID: notificationStatusID,
+ pagingSize: pagingSize,
+ currentPage: currentPage,
+ );
+
+ result.fold(
+ (failure) async {
+ isNotificationsLoading = false;
+ notifyListeners();
+ await errorHandlerService.handleError(failure: failure);
+ if (onError != null) {
+ onError(failure.toString());
+ }
+ },
+ (apiResponse) {
+ isNotificationsLoading = false;
+ if (apiResponse.messageStatus == 2) {
+ notifyListeners();
+ if (onError != null) {
+ onError(apiResponse.errorMessage ?? "Unknown error");
+ }
+ } else if (apiResponse.messageStatus == 1) {
+ final newNotifications = apiResponse.data ?? [];
+
+ if (newNotifications.isEmpty || newNotifications.length < pagingSize) {
+ hasMoreNotifications = false;
+ }
+
+ notificationsList.addAll(newNotifications);
+ currentPage++;
+
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+
+ Future refreshNotifications({
+ Function(dynamic)? onSuccess,
+ Function(String)? onError,
+ }) async {
+ notificationsList.clear();
+ currentPage = 0;
+ hasMoreNotifications = true;
+ await getAllNotifications(onSuccess: onSuccess, onError: onError);
+ }
+
+ Future loadMoreNotifications({
+ Function(dynamic)? onSuccess,
+ Function(String)? onError,
+ }) async {
+ await getAllNotifications(onSuccess: onSuccess, onError: onError);
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+}
diff --git a/lib/features/todo_section/todo_section_repo.dart b/lib/features/todo_section/todo_section_repo.dart
index 008b22cd..76a44382 100644
--- a/lib/features/todo_section/todo_section_repo.dart
+++ b/lib/features/todo_section/todo_section_repo.dart
@@ -48,6 +48,8 @@ abstract class TodoSectionRepo {
});
Future> applePayInsertRequest({required dynamic applePayInsertRequest});
+
+ Future> getPatientDashboard();
}
class TodoSectionRepoImp implements TodoSectionRepo {
@@ -376,4 +378,29 @@ class TodoSectionRepoImp implements TodoSectionRepo {
return Left(UnknownFailure(e.toString()));
}
}
+
+ @override
+ Future> getPatientDashboard() async {
+ Map mapDevice = {};
+
+ try {
+ dynamic apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ DASHBOARD,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ apiResponse = response;
+ },
+ );
+ if (failure != null) return Left(failure!);
+ return Right(apiResponse);
+ } catch (e) {
+ loggerService.logError("Unknown error in applePayInsertRequest: ${e.toString()}");
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
}
diff --git a/lib/features/todo_section/todo_section_view_model.dart b/lib/features/todo_section/todo_section_view_model.dart
index c0fb96ba..92d893ad 100644
--- a/lib/features/todo_section/todo_section_view_model.dart
+++ b/lib/features/todo_section/todo_section_view_model.dart
@@ -10,11 +10,15 @@ class TodoSectionViewModel extends ChangeNotifier {
TodoSectionViewModel({required this.todoSectionRepo, required this.errorHandlerService});
+ String? notificationsCount = "0";
+
initializeTodoSectionViewModel() async {
patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true;
isAncillaryDetailsProceduresLoading = true;
- await getPatientOnlineAncillaryOrderList();
+ notificationsCount = "0";
+ getPatientOnlineAncillaryOrderList();
+ getPatientDashboard();
}
bool isAncillaryOrdersLoading = false;
@@ -28,6 +32,28 @@ class TodoSectionViewModel extends ChangeNotifier {
notifyListeners();
}
+ Future getPatientDashboard({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ final result = await todoSectionRepo.getPatientDashboard();
+
+ result.fold(
+ (failure) async {
+ await errorHandlerService.handleError(failure: failure);
+ },
+ (apiResponse) {
+ // if (apiResponse.messageStatus == 2) {
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ // } else if (apiResponse.messageStatus == 1) {
+ notificationsCount =
+ apiResponse['List_PatientDashboard'][0]['UnreadPatientNotificationCount'] > 99 ? '99+' : apiResponse['List_PatientDashboard'][0]['UnreadPatientNotificationCount'].toString();
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ // }
+ },
+ );
+ }
+
Future getPatientOnlineAncillaryOrderList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true;
diff --git a/lib/main.dart b/lib/main.dart
index eb57c374..0224afa3 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -27,6 +27,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/appointment_rating_
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_view_model.dart';
+import 'package:hmg_patient_app_new/features/notifications/notifications_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
@@ -177,6 +178,9 @@ void main() async {
),
ChangeNotifierProvider(
create: (_) => getIt.get(),
+ ),
+ ChangeNotifierProvider(
+ create: (_) => getIt.get(),
)
], child: MyApp()),
),
diff --git a/lib/presentation/home/data/landing_page_data.dart b/lib/presentation/home/data/landing_page_data.dart
index c6934790..ef2832b1 100644
--- a/lib/presentation/home/data/landing_page_data.dart
+++ b/lib/presentation/home/data/landing_page_data.dart
@@ -46,7 +46,7 @@ class LandingPageData {
),
ServiceCardData(
serviceName: "health_converters",
- icon: AppAssets.health_calculators_icon,
+ icon: AppAssets.health_converters_icon,
title: "Health",
subtitle: "Converters",
backgroundColor: AppColors.whiteColor,
@@ -56,7 +56,7 @@ class LandingPageData {
),
ServiceCardData(
serviceName: "parking_guide",
- icon: AppAssets.health_calculators_icon,
+ icon: AppAssets.car_parking_icon,
title: "Parking",
subtitle: "Guide",
backgroundColor: AppColors.whiteColor,
diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart
index b25ae823..a0dd27f1 100644
--- a/lib/presentation/home/landing_page.dart
+++ b/lib/presentation/home/landing_page.dart
@@ -21,11 +21,13 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
+import 'package:hmg_patient_app_new/features/hospital/hospital_selection_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_rating_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/features/notifications/notifications_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
@@ -44,6 +46,7 @@ import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card
import 'package:hmg_patient_app_new/presentation/home/widgets/small_service_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/welcome_widget.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
+import 'package:hmg_patient_app_new/presentation/notifications/notifications_list_page.dart';
import 'package:hmg_patient_app_new/presentation/profile_settings/profile_settings.dart';
import 'package:hmg_patient_app_new/presentation/rate_appointment/rate_appointment_doctor.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/ancillary_procedures_details_page.dart';
@@ -59,6 +62,8 @@ import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/routes/spring_page_route_builder.dart';
import 'package:provider/provider.dart';
+import '../emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart';
+
class LandingPage extends StatefulWidget {
const LandingPage({super.key});
@@ -80,6 +85,7 @@ class _LandingPageState extends State {
late BookAppointmentsViewModel bookAppointmentsViewModel;
late EmergencyServicesViewModel emergencyServicesViewModel;
late TodoSectionViewModel todoSectionViewModel;
+ late NotificationsViewModel notificationsViewModel;
final SwiperController _controller = SwiperController();
@@ -106,22 +112,26 @@ class _LandingPageState extends State {
myAppointmentsViewModel.getPatientAppointments(true, false);
emergencyServicesViewModel.checkPatientERAdvanceBalance();
myAppointmentsViewModel.getPatientAppointmentQueueDetails();
- if(!appState.isRatedVisible) {
- appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) {
- if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) {
- appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!,
- onSuccess: ((response) {
- appointmentRatingViewModel.setClinicOrDoctor(false);
- appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation);
- appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation);
- openLastRating();
- appState.setRatedVisible(true);
- }),
- );
- }
- },
- );
- }
+ notificationsViewModel.initNotificationsViewModel();
+
+ // Commented as per new requirement to remove rating popup from the app
+
+ // if(!appState.isRatedVisible) {
+ // appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) {
+ // if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) {
+ // appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!,
+ // onSuccess: ((response) {
+ // appointmentRatingViewModel.setClinicOrDoctor(false);
+ // appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation);
+ // appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation);
+ // openLastRating();
+ // appState.setRatedVisible(true);
+ // }),
+ // );
+ // }
+ // },
+ // );
+ // }
}
});
super.initState();
@@ -135,6 +145,7 @@ class _LandingPageState extends State {
immediateLiveCareViewModel = Provider.of(context, listen: false);
emergencyServicesViewModel = Provider.of(context, listen: false);
todoSectionViewModel = Provider.of(context, listen: false);
+ notificationsViewModel = Provider.of(context, listen: false);
appState = getIt.get();
return PopScope(
canPop: false,
@@ -180,37 +191,65 @@ class _LandingPageState extends State {
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
height: 40.h,
),
- Row(
- mainAxisSize: MainAxisSize.min,
- spacing: 12.h,
- children: [
- Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 18.h, width: 18.h).onPress(() {
- Navigator.of(context).push(
- CustomPageRoute(
- page: MedicalFilePage(),
- // page: LoginScreen(),
- ),
- );
- }),
- Utils.buildSvgWithAssets(icon: AppAssets.indoor_nav_icon, height: 18.h, width: 18.h).onPress(() {
- // Navigator.of(context).push(
- // CustomPageRoute(
- // page: MedicalFilePage(),
- // // page: LoginScreen(),
- // ),
- // );
- }),
- Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 18.h, width: 18.h).onPress(() {
- showCommonBottomSheetWithoutHeight(
- context,
- title: LocaleKeys.contactUs.tr(),
- child: ContactUs(),
- callBackFunc: () {},
- isFullScreen: false,
- );
- }),
- ],
- ),
+ Consumer(builder: (context, todoSectionVM, child) {
+ return Row(
+ mainAxisSize: MainAxisSize.min,
+ spacing: 12.h,
+ children: [
+ Stack(children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 18.h, width: 18.h).onPress(() async {
+ if (appState.isAuthenticated) {
+ notificationsViewModel.setNotificationStatusID(2);
+ notificationsViewModel.getAllNotifications();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: NotificationsListPage(),
+ // page: LoginScreen(),
+ ),
+ );
+ } else {
+ await authVM.onLoginPressed();
+ }
+ }),
+ (appState.isAuthenticated && (int.parse(todoSectionVM.notificationsCount ?? "0") > 0))
+ ? Positioned(
+ right: 0,
+ top: 0,
+ child: Container(
+ width: 8.w,
+ height: 8.h,
+ padding: EdgeInsets.all(4),
+ decoration: BoxDecoration(
+ color: AppColors.primaryRedColor,
+ borderRadius: BorderRadius.circular(20.r),
+ ),
+ child: Text(
+ "",
+ style: TextStyle(
+ color: Colors.white,
+ fontSize: 8.f,
+ ),
+ textAlign: TextAlign.center,
+ ),
+ ),
+ )
+ : SizedBox.shrink(),
+ ]),
+ Utils.buildSvgWithAssets(icon: AppAssets.indoor_nav_icon, height: 18.h, width: 18.h).onPress(() {
+ openIndoorNavigationBottomSheet(context);
+ }),
+ Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 18.h, width: 18.h).onPress(() {
+ showCommonBottomSheetWithoutHeight(
+ context,
+ title: LocaleKeys.contactUs.tr(),
+ child: ContactUs(),
+ callBackFunc: () {},
+ isFullScreen: false,
+ );
+ }),
+ ],
+ );
+ }),
],
).paddingSymmetrical(24.h, 0.h),
!appState.isAuthenticated
@@ -329,208 +368,7 @@ class _LandingPageState extends State {
builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor),
),
itemBuilder: (BuildContext context, int index) {
- return (myAppointmentsVM.isPatientHasQueueAppointment && index == 0)
- ? Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 20.h,
- hasShadow: false,
- side: BorderSide(color: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), width: 2.w),
- ),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- AppCustomChipWidget(
- labelText: myAppointmentsVM.currentQueueStatus == 0 ? "In Queue".needTranslation : "Your Turn".needTranslation,
- backgroundColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.20),
- textColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus),
- ),
- Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h),
- ],
- ),
- SizedBox(height: 8.h),
- "Hala ${appState.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true),
- SizedBox(height: 2.h),
- "Thank you for your patience, here is your queue number."
- .needTranslation
- .toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
- SizedBox(height: 8.h),
- myAppointmentsVM.currentPatientQueueDetails.queueNo!.toText28(isBold: true),
- SizedBox(height: 6.h),
- myAppointmentsVM.patientQueueDetailsList.isNotEmpty ? Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- "Serving Now: ".needTranslation.toText14(isBold: true),
- Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- myAppointmentsVM.patientQueueDetailsList.first.queueNo!.toText12(isBold: true),
- SizedBox(width: 8.w),
- AppCustomChipWidget(
- deleteIcon: myAppointmentsVM.patientQueueDetailsList.first.callType == 1 ? AppAssets.call_for_vitals : AppAssets.call_for_doctor,
- labelText: myAppointmentsVM.patientQueueDetailsList.first.callType == 1 ? "Call for vital signs".needTranslation : "Call for Doctor".needTranslation,
- iconColor: myAppointmentsVM.patientQueueDetailsList.first.callType == 1 ? AppColors.primaryRedColor : AppColors.successColor,
- textColor: myAppointmentsVM.patientQueueDetailsList.first.callType == 1 ? AppColors.primaryRedColor : AppColors.successColor,
- iconSize: 14.w,
- backgroundColor: myAppointmentsVM.patientQueueDetailsList.first.callType == 1 ? AppColors.primaryRedColor.withValues(alpha: 0.1) : AppColors.successColor.withValues(alpha: 0.1),
- labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: -2.h),
- ),
- ],
- ),
- ],
- ) : SizedBox(height: 12.h),
- SizedBox(height: 5.h),
- CustomButton(
- text: Utils.getCardButtonText(myAppointmentsVM.currentQueueStatus, myAppointmentsVM.currentPatientQueueDetails.roomNo ?? ""),
- onPressed: () {},
- backgroundColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus),
- borderColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.01),
- textColor: Utils.getCardButtonTextColor(myAppointmentsVM.currentQueueStatus),
- fontSize: 12.f,
- fontWeight: FontWeight.w600,
- borderRadius: 12.r,
- padding: EdgeInsets.symmetric(horizontal: 10.w),
- height: 40.h,
- iconColor: AppColors.whiteColor,
- iconSize: 18.h,
- ),
- ],
- ),
- ),
- ).onPress(() {
- Navigator.of(context).push(
- CustomPageRoute(
- page: AppointmentQueuePage(),
- ),
- );
- })
- : (immediateLiveCareVM.patientHasPendingLiveCareRequest && index == 0)
- ? Column(
- children: [
- SizedBox(height: 12.h),
- Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 20.r,
- hasShadow: true,
- side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h),
- ),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- "Immediate LiveCare Request".needTranslation.toText16(isBold: true),
- SizedBox(height: 10.h),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Row(
- children: [
- AppCustomChipWidget(
- labelText: immediateLiveCareVM.patientLiveCareHistoryList[0].stringCallStatus,
- backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20),
- textColor: AppColors.alertColor,
- ),
- SizedBox(width: 8.w),
- AppCustomChipWidget(
- icon: AppAssets.appointment_calendar_icon,
- labelText: DateUtil.formatDateToDate(
- DateUtil.convertStringToDate(immediateLiveCareVM.patientLiveCareHistoryList[0].arrivalTime), false)),
- ],
- ),
- Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h),
- // Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.cover),
- ],
- ),
- SizedBox(height: 10.h),
- "Hala ${appState.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true),
- SizedBox(height: 8.h),
- "Your turn is after ${immediateLiveCareVM.patientLiveCareHistoryList[0].patCount} patients.".needTranslation.toText14(isBold: true),
- SizedBox(height: 8.h),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- "Expected waiting time: ".needTranslation.toText12(isBold: true),
- SizedBox(height: 7.h),
- ValueListenableBuilder(
- valueListenable: immediateLiveCareVM.durationNotifier,
- builder: (context, duration, child) {
- return Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- buildTime(duration),
- ],
- );
- },
- ),
- ],
- ),
- // CustomButton(
- // text: "View Details".needTranslation,
- // onPressed: () async {
- // Navigator.of(context).push(CustomPageRoute(page: ImmediateLiveCarePendingRequestPage()));
- // },
- // backgroundColor: Color(0xffFEE9EA),
- // borderColor: Color(0xffFEE9EA),
- // textColor: Color(0xffED1C2B),
- // fontSize: 14.f,
- // fontWeight: FontWeight.w500,
- // borderRadius: 12.r,
- // padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
- // height: 40.h,
- // ),
- ],
- ),
- ),
- ).paddingSymmetrical(0.h, 0.h).onPress(() {
- Navigator.of(context).push(CustomPageRoute(page: ImmediateLiveCarePendingRequestPage()));
- }),
- SizedBox(height: 12.h),
- ],
- )
- : (todoSectionVM.patientAncillaryOrdersList.isNotEmpty && index == 1)
- ? AncillaryOrderCard(
- order: todoSectionVM.patientAncillaryOrdersList.first,
- isLoading: false,
- isOrdersList: false,
- onCheckIn: () {
- log("Check-in for order: ${todoSectionVM.patientAncillaryOrdersList.first.orderNo}");
- },
- onViewDetails: () {
- Navigator.of(context).push(
- CustomPageRoute(
- page: AncillaryOrderDetailsList(
- appointmentNoVida: todoSectionVM.patientAncillaryOrdersList.first.appointmentNo ?? 0,
- orderNo: todoSectionVM.patientAncillaryOrdersList.first.orderNo ?? 0,
- projectID: todoSectionVM.patientAncillaryOrdersList.first.projectID ?? 0,
- projectName: todoSectionVM.patientAncillaryOrdersList.first.projectName ?? "",
- ),
- ),
- );
- },
- )
- : Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24.r,
- hasShadow: true,
- ),
- child: AppointmentCard(
- patientAppointmentHistoryResponseModel:
- myAppointmentsVM.patientAppointmentsHistoryList[immediateLiveCareViewModel.patientHasPendingLiveCareRequest ? --index : index],
- myAppointmentsViewModel: myAppointmentsViewModel,
- bookAppointmentsViewModel: bookAppointmentsViewModel,
- isLoading: false,
- isFromHomePage: true,
- ),
- );
+ return getIndexSwiperCard(index);
},
)
: Container(
@@ -771,6 +609,288 @@ class _LandingPageState extends State {
);
}
+ Widget getIndexSwiperCard(int index) {
+ if (index == 0) {
+ if (myAppointmentsViewModel.isPatientHasQueueAppointment) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ side: BorderSide(color: Utils.getCardBorderColor(myAppointmentsViewModel.currentQueueStatus), width: 2.w),
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ AppCustomChipWidget(
+ labelText: myAppointmentsViewModel.currentQueueStatus == 0 ? "In Queue".needTranslation : "Your Turn".needTranslation,
+ backgroundColor: Utils.getCardBorderColor(myAppointmentsViewModel.currentQueueStatus).withValues(alpha: 0.20),
+ textColor: Utils.getCardBorderColor(myAppointmentsViewModel.currentQueueStatus),
+ ),
+ Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ "Hala ${appState.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true),
+ SizedBox(height: 2.h),
+ "Thank you for your patience, here is your queue number.".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
+ SizedBox(height: 8.h),
+ myAppointmentsViewModel.currentPatientQueueDetails.queueNo!.toText28(isBold: true),
+ SizedBox(height: 6.h),
+ myAppointmentsViewModel.patientQueueDetailsList.isNotEmpty
+ ? Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ "Serving Now: ".needTranslation.toText14(isBold: true),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ myAppointmentsViewModel.patientQueueDetailsList.first.queueNo!.toText12(isBold: true),
+ SizedBox(width: 8.w),
+ AppCustomChipWidget(
+ deleteIcon: myAppointmentsViewModel.patientQueueDetailsList.first.callType == 1 ? AppAssets.call_for_vitals : AppAssets.call_for_doctor,
+ labelText: myAppointmentsViewModel.patientQueueDetailsList.first.callType == 1 ? "Call for vital signs".needTranslation : "Call for Doctor".needTranslation,
+ iconColor: myAppointmentsViewModel.patientQueueDetailsList.first.callType == 1 ? AppColors.primaryRedColor : AppColors.successColor,
+ textColor: myAppointmentsViewModel.patientQueueDetailsList.first.callType == 1 ? AppColors.primaryRedColor : AppColors.successColor,
+ iconSize: 14.w,
+ backgroundColor: myAppointmentsViewModel.patientQueueDetailsList.first.callType == 1
+ ? AppColors.primaryRedColor.withValues(alpha: 0.1)
+ : AppColors.successColor.withValues(alpha: 0.1),
+ labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: -2.h),
+ ),
+ ],
+ ),
+ ],
+ )
+ : SizedBox(height: 12.h),
+ SizedBox(height: 5.h),
+ CustomButton(
+ text: Utils.getCardButtonText(myAppointmentsViewModel.currentQueueStatus, myAppointmentsViewModel.currentPatientQueueDetails.roomNo ?? ""),
+ onPressed: () {},
+ backgroundColor: Utils.getCardButtonColor(myAppointmentsViewModel.currentQueueStatus),
+ borderColor: Utils.getCardButtonColor(myAppointmentsViewModel.currentQueueStatus).withValues(alpha: 0.01),
+ textColor: Utils.getCardButtonTextColor(myAppointmentsViewModel.currentQueueStatus),
+ fontSize: 12.f,
+ fontWeight: FontWeight.w600,
+ borderRadius: 12.r,
+ padding: EdgeInsets.symmetric(horizontal: 10.w),
+ height: 40.h,
+ iconColor: AppColors.whiteColor,
+ iconSize: 18.h,
+ ),
+ ],
+ ),
+ ),
+ ).onPress(() {
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: AppointmentQueuePage(),
+ ),
+ );
+ });
+ } else if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) {
+ return Column(
+ children: [
+ SizedBox(height: 12.h),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.r,
+ hasShadow: true,
+ side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h),
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Immediate LiveCare Request".needTranslation.toText16(isBold: true),
+ SizedBox(height: 10.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ AppCustomChipWidget(
+ labelText: immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus,
+ backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20),
+ textColor: AppColors.alertColor,
+ ),
+ SizedBox(width: 8.w),
+ AppCustomChipWidget(
+ icon: AppAssets.appointment_calendar_icon,
+ labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(immediateLiveCareViewModel.patientLiveCareHistoryList[0].arrivalTime), false)),
+ ],
+ ),
+ Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h),
+ // Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.cover),
+ ],
+ ),
+ SizedBox(height: 10.h),
+ "Hala ${appState.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true),
+ SizedBox(height: 8.h),
+ "Your turn is after ${immediateLiveCareViewModel.patientLiveCareHistoryList[0].patCount} patients.".needTranslation.toText14(isBold: true),
+ SizedBox(height: 8.h),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Expected waiting time: ".needTranslation.toText12(isBold: true),
+ SizedBox(height: 7.h),
+ ValueListenableBuilder(
+ valueListenable: immediateLiveCareViewModel.durationNotifier,
+ builder: (context, duration, child) {
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ buildTime(duration),
+ ],
+ );
+ },
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ).paddingSymmetrical(0.h, 0.h).onPress(() {
+ Navigator.of(context).push(CustomPageRoute(page: ImmediateLiveCarePendingRequestPage()));
+ }),
+ SizedBox(height: 12.h),
+ ],
+ );
+ } else if (myAppointmentsViewModel.patientUpcomingAppointmentsHistoryList.isNotEmpty) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: AppointmentCard(
+ patientAppointmentHistoryResponseModel: myAppointmentsViewModel.patientUpcomingAppointmentsHistoryList.first,
+ myAppointmentsViewModel: myAppointmentsViewModel,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ isLoading: false,
+ isFromHomePage: true,
+ ),
+ );
+ } else {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: AppointmentCard(
+ patientAppointmentHistoryResponseModel: myAppointmentsViewModel.patientAppointmentsHistoryList[index],
+ myAppointmentsViewModel: myAppointmentsViewModel,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ isLoading: false,
+ isFromHomePage: true,
+ ),
+ );
+ }
+ } else if (index == 1) {
+ if (myAppointmentsViewModel.isPatientHasQueueAppointment || immediateLiveCareViewModel.patientHasPendingLiveCareRequest) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: AppointmentCard(
+ patientAppointmentHistoryResponseModel: myAppointmentsViewModel.patientUpcomingAppointmentsHistoryList.first,
+ myAppointmentsViewModel: myAppointmentsViewModel,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ isLoading: false,
+ isFromHomePage: true,
+ ),
+ );
+ } else if (todoSectionViewModel.patientAncillaryOrdersList.isNotEmpty) {
+ return AncillaryOrderCard(
+ order: todoSectionViewModel.patientAncillaryOrdersList.first,
+ isLoading: false,
+ isOrdersList: false,
+ onCheckIn: () {
+ log("Check-in for order: ${todoSectionViewModel.patientAncillaryOrdersList.first.orderNo}");
+ },
+ onViewDetails: () {
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: AncillaryOrderDetailsList(
+ appointmentNoVida: todoSectionViewModel.patientAncillaryOrdersList.first.appointmentNo ?? 0,
+ orderNo: todoSectionViewModel.patientAncillaryOrdersList.first.orderNo ?? 0,
+ projectID: todoSectionViewModel.patientAncillaryOrdersList.first.projectID ?? 0,
+ projectName: todoSectionViewModel.patientAncillaryOrdersList.first.projectName ?? "",
+ ),
+ ),
+ );
+ },
+ );
+ } else {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: AppointmentCard(
+ patientAppointmentHistoryResponseModel: myAppointmentsViewModel.patientAppointmentsHistoryList[index],
+ myAppointmentsViewModel: myAppointmentsViewModel,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ isLoading: false,
+ isFromHomePage: true,
+ ),
+ );
+ }
+ } else if (index == 2) {
+ if ((myAppointmentsViewModel.isPatientHasQueueAppointment || immediateLiveCareViewModel.patientHasPendingLiveCareRequest) &&
+ myAppointmentsViewModel.patientUpcomingAppointmentsHistoryList.isNotEmpty) {
+ return AncillaryOrderCard(
+ order: todoSectionViewModel.patientAncillaryOrdersList.first,
+ isLoading: false,
+ isOrdersList: false,
+ onCheckIn: () {
+ log("Check-in for order: ${todoSectionViewModel.patientAncillaryOrdersList.first.orderNo}");
+ },
+ onViewDetails: () {
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: AncillaryOrderDetailsList(
+ appointmentNoVida: todoSectionViewModel.patientAncillaryOrdersList.first.appointmentNo ?? 0,
+ orderNo: todoSectionViewModel.patientAncillaryOrdersList.first.orderNo ?? 0,
+ projectID: todoSectionViewModel.patientAncillaryOrdersList.first.projectID ?? 0,
+ projectName: todoSectionViewModel.patientAncillaryOrdersList.first.projectName ?? "",
+ ),
+ ),
+ );
+ },
+ );
+ } else {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: AppointmentCard(
+ patientAppointmentHistoryResponseModel: myAppointmentsViewModel.patientAppointmentsHistoryList[index],
+ myAppointmentsViewModel: myAppointmentsViewModel,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ isLoading: false,
+ isFromHomePage: true,
+ ),
+ );
+ }
+ }
+ return Container();
+ }
+
void showQuickLogin(BuildContext context) {
showCommonBottomSheetWithoutHeight(
context,
@@ -829,4 +949,41 @@ class _LandingPageState extends State {
isFullScreen: false,
);
}
+
+ void openIndoorNavigationBottomSheet(BuildContext context) {
+ showCommonBottomSheetWithoutHeight(
+ title: LocaleKeys.selectHospital.tr(),
+ context,
+ child: ChangeNotifierProvider(
+ create: (context) => HospitalSelectionBottomSheetViewModel(getIt()),
+ child: Consumer(
+ builder: (_, vm, __) => HospitalBottomSheetBody(
+ searchText: vm.searchController,
+ displayList: vm.displayList,
+ onFacilityClicked: (value) {
+ vm.setSelectedFacility(value);
+ vm.getDisplayList();
+ },
+ onHospitalClicked: (hospital) {
+ Navigator.pop(context);
+ vm.openPenguin(hospital);
+ },
+ onHospitalSearch: (value) {
+ vm.searchHospitals(value ?? "");
+ },
+ selectedFacility: vm.selectedFacility,
+ hmcCount: vm.hmcCount,
+ hmgCount: vm.hmgCount,
+ ),
+ ),
+ ),
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ hasBottomPadding: false,
+ backgroundColor: AppColors.bottomSheetBgColor,
+ callBackFunc: () {
+ context.read().clearSearchText();
+ },
+ );
+ }
}
diff --git a/lib/presentation/home/widgets/small_service_card.dart b/lib/presentation/home/widgets/small_service_card.dart
index 62168808..e74100c7 100644
--- a/lib/presentation/home/widgets/small_service_card.dart
+++ b/lib/presentation/home/widgets/small_service_card.dart
@@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
@@ -8,7 +9,9 @@ import 'package:hmg_patient_app_new/features/emergency_services/emergency_servic
import 'package:hmg_patient_app_new/features/hospital/hospital_selection_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/emergency_services_page.dart';
+import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculators_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart';
@@ -126,6 +129,28 @@ class SmallServiceCard extends StatelessWidget {
case "indoor_navigation":
openIndoorNavigationBottomSheet(context);
+
+ case "search_doctor":
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: SearchDoctorByName(),
+ ),
+ );
+ break;
+ case "health_calculators_and_converts":
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: HealthCalculatorsPage(type: HealthCalConEnum.calculator),
+ ),
+ );
+ break;
+ case "health_converters":
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: HealthCalculatorsPage(type: HealthCalConEnum.converter),
+ ),
+ );
+ break;
default:
// Handle unknown service
break;
diff --git a/lib/presentation/notifications/notifications_list_page.dart b/lib/presentation/notifications/notifications_list_page.dart
new file mode 100644
index 00000000..532b5836
--- /dev/null
+++ b/lib/presentation/notifications/notifications_list_page.dart
@@ -0,0 +1,72 @@
+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/extensions/int_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/notifications/notifications_view_model.dart';
+import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+import 'package:provider/provider.dart';
+
+class NotificationsListPage extends StatelessWidget {
+ const NotificationsListPage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return CollapsingListView(
+ title: "Notifications".needTranslation,
+ child: SingleChildScrollView(
+ child: Consumer(builder: (context, notificationsVM, child) {
+ return Container(
+ margin: EdgeInsets.symmetric(vertical: 24.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.h,
+ hasShadow: false,
+ ),
+ child: ListView.builder(
+ itemCount: notificationsVM.isNotificationsLoading ? 4 : notificationsVM.notificationsList.length,
+ physics: NeverScrollableScrollPhysics(),
+ shrinkWrap: true,
+ padding: EdgeInsetsGeometry.zero,
+ itemBuilder: (context, index) {
+ return notificationsVM.isNotificationsLoading
+ ? LabResultItemView(
+ onTap: () {},
+ labOrder: null,
+ index: index,
+ isLoading: true,
+ )
+ : AnimationConfiguration.staggeredList(
+ position: index,
+ duration: const Duration(milliseconds: 500),
+ child: SlideAnimation(
+ verticalOffset: 100.0,
+ child: FadeInAnimation(
+ child: AnimatedContainer(
+ duration: Duration(milliseconds: 300),
+ curve: Curves.easeInOut,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 16.h),
+ "Notification Title".toText14(),
+ SizedBox(height: 8.h),
+ notificationsVM.notificationsList[index].message!.toText14(),
+ SizedBox(height: 12.h),
+ 1.divider,
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }).paddingSymmetrical(16.w, 0.h),
+ ).paddingSymmetrical(24.w, 0.h);
+ }),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/todo_section/ancillary_order_payment_page.dart b/lib/presentation/todo_section/ancillary_order_payment_page.dart
index fdfcc04a..054108df 100644
--- a/lib/presentation/todo_section/ancillary_order_payment_page.dart
+++ b/lib/presentation/todo_section/ancillary_order_payment_page.dart
@@ -266,7 +266,11 @@ class _AncillaryOrderPaymentPageState extends State {
fit: BoxFit.contain,
).paddingSymmetrical(24.h, 0.h).onPress(() {
if (!todoSectionViewModel.isProcessingPayment) {
- _startApplePay();
+ if (Utils.havePrivilege(103)) {
+ _startApplePay();
+ } else {
+ _openPaymentURL("ApplePay");
+ }
}
})
: SizedBox(height: 12.h),
diff --git a/lib/widgets/appbar/collapsing_list_view.dart b/lib/widgets/appbar/collapsing_list_view.dart
index 5409fcf5..0580776c 100644
--- a/lib/widgets/appbar/collapsing_list_view.dart
+++ b/lib/widgets/appbar/collapsing_list_view.dart
@@ -89,7 +89,7 @@ class CollapsingListView extends StatelessWidget {
? Transform.flip(
flipX: appState.isArabic(),
child: IconButton(
- icon: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.closeBottomNav : AppAssets.arrow_back, width: 24.h, height: 24.h),
+ icon: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.closeBottomNav : AppAssets.forward_top_nav_icon, width: 24.h, height: 24.h),
padding: EdgeInsets.only(left: 12),
onPressed: () {
if (leadingCallback != null) {