diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 2987d3b..5d74c4d 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -160,6 +160,7 @@ dependencies {
annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
implementation("com.mapbox.maps:android:11.5.0")
+ implementation("com.mapbox.mapboxsdk:mapbox-sdk-turf:7.3.1")
// implementation("com.mapbox.maps:android:11.4.0")
// AARs
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 b822d67..70889d3 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 0000000..62bf96b
--- /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 0000000..089161c
--- /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 31cf626..5b9934b 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 d5303e2..508fb74 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 4acf486..efefee1 100644
--- a/lib/core/api_consts.dart
+++ b/lib/core/api_consts.dart
@@ -399,6 +399,22 @@ 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';
+var ADD_DIABTEC_RESULT = 'Services/Patients.svc/REST/Patient_AddDiabtecResult';
+
+var GET_BLOOD_PRESSURE_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetBloodPressureResultAverage';
+var GET_BLOOD_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_GetBloodPressureResult';
+var ADD_BLOOD_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_AddBloodPressureResult';
+
+var GET_WEIGHT_PRESSURE_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResultAverage';
+var GET_WEIGHT_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResult';
+var ADD_WEIGHT_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_AddWeightMeasurementResult';
+
var ADD_ACTIVE_PRESCRIPTIONS_REPORT_BY_PATIENT_ID = 'Services/Patients.svc/Rest/GetActivePrescriptionReportByPatientID';
var GET_CALL_INFO_HOURS_RESULT = 'Services/Doctors.svc/REST/GetCallInfoHoursResult';
@@ -659,6 +675,8 @@ 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;
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index d378c78..26da9fa 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -225,6 +225,9 @@ class AppAssets {
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 bdcc818..6c7d358 100644
--- a/lib/core/dependencies.dart
+++ b/lib/core/dependencies.dart
@@ -42,6 +42,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';
@@ -163,13 +165,14 @@ class AppDependencies {
),);
getIt.registerLazySingleton(() => MonthlyReportsRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => QrParkingRepoImp(loggerService: getIt(), apiClient: getIt()));
- // getIt.registerFactory(
- // () => QrParkingViewModel(
- // qrParkingRepo: getIt(),
- // errorHandlerService: getIt(),
- // cacheService: getIt(),
- // ),
- // );
+ getIt.registerFactory(
+ () => QrParkingViewModel(
+ qrParkingRepo: getIt(),
+ errorHandlerService: getIt(),
+ cacheService: getIt(),
+ ),
+ );
+ getIt.registerLazySingleton(() => NotificationsRepoImp(loggerService: getIt(), apiClient: getIt()));
// ViewModels
// Global/shared VMs → LazySingleton
@@ -289,14 +292,12 @@ class AppDependencies {
getIt.registerLazySingleton(() => WaterMonitorViewModel(waterMonitorRepo: getIt(), errorHandlerService: getIt()));
//commenting this because its already define there was on run time error because of this.
- // getIt.registerLazySingleton(() => MyInvoicesViewModel(myInvoicesRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
+ getIt.registerLazySingleton(() => MyInvoicesViewModel(myInvoicesRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton(() => MonthlyReportViewModel(errorHandlerService: getIt(), monthlyReportRepo: getIt()));
- getIt.registerLazySingleton(() => MyInvoicesViewModel(
- myInvoicesRepo: getIt(),
- errorHandlerService: getIt(),
- navServices: getIt(),
- ));
+
+ getIt.registerLazySingleton(() => NotificationsViewModel(notificationsRepo: getIt(), errorHandlerService: getIt()));
+
getIt.registerLazySingleton(() => HealthTrackersViewModel(healthTrackersRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton(
() => ActivePrescriptionsViewModel(
@@ -304,13 +305,5 @@ class AppDependencies {
activePrescriptionsRepo: getIt()
),
);
- getIt.registerFactory(
- () => QrParkingViewModel(
- qrParkingRepo: getIt(),
- errorHandlerService: getIt(),
- cacheService: getIt(),
- ),
- );
-
}
}
diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart
index f5ffd36..8978fcd 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",
@@ -55,19 +55,20 @@ class Utils {
"IsActive": true,
"IsHmg": true,
"IsVidaPlus": false,
- "Latitude": "24.8113774",
- "Longitude": "46.6239813",
+ "Latitude": "24.8111548",
+ "Longitude": "46.6217912",
"MainProjectID": 130,
"ProjectOutSA": false,
- "UsingInDoctorApp": false
+ "UsingInDoctorApp": false,
+ "IsHMC": false
},{
- "Desciption": "Jeddah Hospital",
- "DesciptionN": "مستشفى الصحافة",
- "ID": 3,
- "LegalName": "Jeddah Hospital",
- "LegalNameN": "مستشفى الصحافة",
- "Name": "Jeddah Hospital",
- "NameN": "مستشفى الصحافة",
+ "Desciption": "Jeddah Fayhaa Hospital",
+ "DesciptionN": "مستشفى جدة الفيحاء",
+ "ID": 3, // Campus ID
+ "LegalName": "Jeddah Fayhaa Hospital",
+ "LegalNameN": "مستشفى جدة الفيحاء",
+ "Name": "Jeddah Fayhaa Hospital",
+ "NameN": "مستشفى جدة الفيحاء",
"PhoneNumber": "+966115222222",
"SetupID": "013311",
"DistanceInKilometers": 0,
@@ -75,11 +76,12 @@ class Utils {
"IsActive": true,
"IsHmg": true,
"IsVidaPlus": false,
- "Latitude": "24.8113774",
- "Longitude": "46.6239813",
+ "Latitude": "21.5086703",
+ "Longitude": "39.2121855",
"MainProjectID": 130,
"ProjectOutSA": false,
- "UsingInDoctorApp": false
+ "UsingInDoctorApp": false,
+ "IsHMC": false
}
];
diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart
index b393772..6379456 100644
--- a/lib/features/authentication/authentication_view_model.dart
+++ b/lib/features/authentication/authentication_view_model.dart
@@ -246,7 +246,6 @@ class AuthenticationViewModel extends ChangeNotifier {
}
Future _handleNewImeiRegistration() async {
-
await selectDeviceImei(onSuccess: (dynamic respData) async {
try {
if (respData != null) {
@@ -461,6 +460,12 @@ class AuthenticationViewModel extends ChangeNotifier {
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true || _appState.getUserRegistrationPayload.patientOutSa == 1);
MyAppointmentsViewModel myAppointmentsVM = getIt();
+ if (isSwitchUser && _appState.getSuperUserID == null) {
+ nationalIdController.text = responseID.toString();
+ }else if( isSwitchUser && _appState.getSuperUserID != null){
+ nationalIdController.text = _appState.getSuperUserID.toString();
+ }
+
final request = RequestUtils.getCommonRequestWelcome(
phoneNumber: phoneNumberController.text,
otpTypeEnum: otpTypeEnum,
@@ -761,14 +766,14 @@ class AuthenticationViewModel extends ChangeNotifier {
phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
: _appState.getAuthenticatedUser()!.mobileNumber)!;
- nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!;
+ nationalIdController.text = _appState.getAuthenticatedUser()!.patientIdentificationNo!;
onSuccess();
} else if ((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) &&
_appState.getAuthenticatedUser() != null) {
phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
: _appState.getAuthenticatedUser()!.mobileNumber)!;
- nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!;
+ nationalIdController.text = _appState.getAuthenticatedUser()!.patientIdentificationNo!;
onSuccess();
}
}
diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart
index aa1467b..3f5517a 100644
--- a/lib/features/book_appointments/book_appointments_view_model.dart
+++ b/lib/features/book_appointments/book_appointments_view_model.dart
@@ -53,6 +53,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
int? calculationID = 0;
bool isSortByClinic = true;
+ // Accordion expansion state
+ int? expandedGroupIndex;
+
int initialSlotDuration = 0;
bool isNearestAppointmentSelected = false;
@@ -158,13 +161,28 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isBodyPartsLoading = false;
int duration = 0;
-
setIsSortByClinic(bool value) {
isSortByClinic = value;
doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
notifyListeners();
}
+ // Toggle accordion expansion
+ void toggleGroupExpansion(int index) {
+ if (expandedGroupIndex == index) {
+ expandedGroupIndex = null;
+ } else {
+ expandedGroupIndex = index;
+ }
+ notifyListeners();
+ }
+
+ // Reset accordion expansion
+ void resetGroupExpansion() {
+ expandedGroupIndex = null;
+ notifyListeners();
+ }
+
// Sort filtered doctor list by clinic or hospital
void sortFilteredDoctorList(bool sortByClinic) {
isSortByClinic = sortByClinic;
@@ -186,6 +204,22 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
+ // Group filtered doctors list for accordion display
+ List> getGroupedFilteredDoctorsList() {
+ final clinicMap = >{};
+ final hospitalMap = >{};
+
+ for (var doctor in filteredDoctorList) {
+ final clinicKey = (doctor.clinicName ?? 'Unknown').trim();
+ clinicMap.putIfAbsent(clinicKey, () => []).add(doctor);
+
+ final hospitalKey = (doctor.projectName ?? 'Unknown').trim();
+ hospitalMap.putIfAbsent(hospitalKey, () => []).add(doctor);
+ }
+
+ return isSortByClinic ? clinicMap.values.toList() : hospitalMap.values.toList();
+ }
+
// Group doctors by clinic and hospital
void _groupDoctorsList() {
final clinicMap = >{};
@@ -237,6 +271,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
isDoctorProfileLoading = true;
isLiveCareSchedule = false;
currentlySelectedHospitalFromRegionFlow = null;
+ expandedGroupIndex = null; // Reset accordion state
clinicsList.clear();
doctorsList.clear();
liveCareClinicsList.clear();
@@ -254,9 +289,24 @@ class BookAppointmentsViewModel extends ChangeNotifier {
this.isNearestAppointmentSelected = isNearestAppointmentSelected;
if (isNearestAppointmentSelected) {
- doctorsList.sort((a, b) => DateUtil.convertStringToDate(a.nearestFreeSlot!).compareTo(DateUtil.convertStringToDate(b.nearestFreeSlot!)));
+ for (var group in doctorsListGrouped) {
+ group.sort((a, b) {
+ var aSlot = a.nearestFreeSlot;
+ var bSlot = b.nearestFreeSlot;
+ if (aSlot == null || bSlot == null) return 0;
+ return DateUtil.convertStringToDate(aSlot).compareTo(DateUtil.convertStringToDate(bSlot));
+ });
+ }
} else {
- doctorsList.sort((a, b) => b.decimalDoctorRate!.compareTo(a.decimalDoctorRate!));
+ for (var group in doctorsListGrouped) {
+ group.sort((a, b) {
+ var aSlot = a.decimalDoctorRate;
+ var bSlot = b.decimalDoctorRate;
+ if (aSlot == null || bSlot == null) return 0;
+ return bSlot.compareTo(aSlot);
+ });
+ }
+ // doctorsList.sort((a, b) => b.decimalDoctorRate!.compareTo(a.decimalDoctorRate!));
}
notifyListeners();
diff --git a/lib/features/hmg_services/hmg_services_view_model.dart b/lib/features/hmg_services/hmg_services_view_model.dart
index c55a11c..daddbff 100644
--- a/lib/features/hmg_services/hmg_services_view_model.dart
+++ b/lib/features/hmg_services/hmg_services_view_model.dart
@@ -40,6 +40,8 @@ class HmgServicesViewModel extends ChangeNotifier {
bool isHospitalListLoading = false;
bool isVitalSignLoading = false;
+ bool hasVitalSignDataLoaded = false;
+
// HHC specific loading states
bool isHhcOrdersLoading = false;
bool isHhcServicesLoading = false;
@@ -878,6 +880,7 @@ class HmgServicesViewModel extends ChangeNotifier {
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
+ if (hasVitalSignDataLoaded) return;
isVitalSignLoading = true;
notifyListeners();
@@ -896,6 +899,7 @@ class HmgServicesViewModel extends ChangeNotifier {
isVitalSignLoading = false;
if (apiResponse.messageStatus == 1) {
vitalSignList = apiResponse.data ?? [];
+ hasVitalSignDataLoaded = true;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
diff --git a/lib/features/hospital/hospital_selection_view_model.dart b/lib/features/hospital/hospital_selection_view_model.dart
index dd9531f..c0cab2a 100644
--- a/lib/features/hospital/hospital_selection_view_model.dart
+++ b/lib/features/hospital/hospital_selection_view_model.dart
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/penguin_method_channel.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/hospital/AppPermission.dart';
@@ -17,20 +19,32 @@ class HospitalSelectionBottomSheetViewModel extends ChangeNotifier {
int hmgCount = 0;
TextEditingController searchController = TextEditingController();
final AppState appState;
+ LocationUtils locationUtils = getIt.get();
HospitalSelectionBottomSheetViewModel(this.appState) {
- Utils.navigationProjectsList.forEach((element) {
- HospitalsModel model = HospitalsModel.fromJson(element);
- if (model.isHMC == true) {
- hmcHospitalList.add(model);
- } else {
- hmgHospitalList.add(model);
- }
- listOfData.add(model);
+ locationUtils.getCurrentLocation(onSuccess: (value) {
+ Utils.navigationProjectsList.forEach((element) {
+ HospitalsModel model = HospitalsModel.fromJson(element);
+
+ double dist = Utils.distance(value.latitude, value.longitude, double.parse(model.latitude!), double.parse(model.longitude!)).ceilToDouble();
+ print(dist);
+ model.distanceInKilometers = dist;
+
+ if (model.isHMC == true) {
+ hmcHospitalList.add(model);
+ } else {
+ hmgHospitalList.add(model);
+ }
+ listOfData.add(model);
+ });
+ hmgCount = hmgHospitalList.length;
+ hmcCount = hmcHospitalList.length;
+
+ listOfData.sort((a, b) => a.distanceInKilometers.compareTo(b.distanceInKilometers));
+ hmgHospitalList.sort((a, b) => a.distanceInKilometers.compareTo(b.distanceInKilometers));
+
+ getDisplayList();
});
- hmgCount = hmgHospitalList.length;
- hmcCount = hmcHospitalList.length;
- getDisplayList();
}
getDisplayList() {
@@ -79,13 +93,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/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart
index 9bd48ec..3934bf7 100644
--- a/lib/features/my_appointments/my_appointments_view_model.dart
+++ b/lib/features/my_appointments/my_appointments_view_model.dart
@@ -726,6 +726,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
}
Future getPatientAppointmentQueueDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ //TODO: Discuss With Haroon, Is the User Has no data it return No Element Bad State;
isAppointmentQueueDetailsLoading = true;
notifyListeners();
final result = await myAppointmentsRepo.getPatientAppointmentQueueDetails(
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 0000000..034e19c
--- /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 0000000..853a979
--- /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 0000000..e15e267
--- /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 008b22c..76a4438 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 c0fb96b..92d893a 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 e730e07..5158706 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -28,6 +28,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';
@@ -184,6 +185,9 @@ void main() async {
ChangeNotifierProvider(
create: (_) => getIt.get(),
),
+ ChangeNotifierProvider(
+ create: (_) => getIt.get(),
+ ),
ChangeNotifierProvider(
create: (_) => getIt.get(),
),
diff --git a/lib/presentation/book_appointment/search_doctor_by_name.dart b/lib/presentation/book_appointment/search_doctor_by_name.dart
index 559e1e5..9f2da16 100644
--- a/lib/presentation/book_appointment/search_doctor_by_name.dart
+++ b/lib/presentation/book_appointment/search_doctor_by_name.dart
@@ -34,6 +34,14 @@ class _SearchDoctorByNameState extends State {
TextEditingController searchEditingController = TextEditingController();
FocusNode textFocusNode = FocusNode();
late BookAppointmentsViewModel bookAppointmentsViewModel;
+ late ScrollController _scrollController;
+ final Map _itemKeys = {};
+
+ @override
+ void initState() {
+ _scrollController = ScrollController();
+ super.initState();
+ }
@override
Widget build(BuildContext context) {
@@ -173,8 +181,11 @@ class _SearchDoctorByNameState extends State {
padding: EdgeInsets.only(top: 20.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
- itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : bookAppointmentsVM.filteredDoctorList.length,
+ itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : bookAppointmentsVM.getGroupedFilteredDoctorsList().length,
itemBuilder: (context, index) {
+ final isExpanded = bookAppointmentsVM.expandedGroupIndex == index;
+ final groupedDoctors = bookAppointmentsVM.getGroupedFilteredDoctorsList();
+
return bookAppointmentsVM.isDoctorsListLoading
? DoctorCard(
doctorsListResponseModel: DoctorsListResponseModel(),
@@ -188,35 +199,112 @@ class _SearchDoctorByNameState extends State {
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
+ key: _itemKeys.putIfAbsent(index, () => GlobalKey()),
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
- child: DoctorCard(
- doctorsListResponseModel: bookAppointmentsVM.filteredDoctorList[index],
- isLoading: false,
- bookAppointmentsViewModel: bookAppointmentsViewModel,
- ).onPress(() async {
- bookAppointmentsVM.setSelectedDoctor(bookAppointmentsVM.filteredDoctorList[index]);
- // bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel());
- LoaderBottomSheet.showLoader();
- await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
- LoaderBottomSheet.hideLoader();
- Navigator.of(context).push(
- CustomPageRoute(
- page: DoctorProfilePage(),
- ),
- );
- }, onError: (err) {
- LoaderBottomSheet.hideLoader();
- showCommonBottomSheetWithoutHeight(
- context,
- child: Utils.getErrorWidget(loadingText: err),
- callBackFunc: () {},
- isFullScreen: false,
- isCloseButtonVisible: true,
- );
- });
- }),
+ child: InkWell(
+ onTap: () {
+ bookAppointmentsVM.toggleGroupExpansion(index);
+ // After rebuild, ensure the expanded item is visible
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ final key = _itemKeys[index];
+ if (key != null && key.currentContext != null && bookAppointmentsVM.expandedGroupIndex == index) {
+ Scrollable.ensureVisible(
+ key.currentContext!,
+ duration: Duration(milliseconds: 350),
+ curve: Curves.easeInOut,
+ alignment: 0.1,
+ );
+ }
+ });
+ },
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ CustomButton(
+ text: "${groupedDoctors[index].length} ${'doctors'.needTranslation}",
+ onPressed: () {},
+ backgroundColor: AppColors.greyColor,
+ borderColor: AppColors.greyColor,
+ textColor: AppColors.blackColor,
+ fontSize: 10,
+ fontWeight: FontWeight.w500,
+ borderRadius: 8,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 30.h),
+ Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ // Clinic/Hospital name as group title
+ Text(
+ bookAppointmentsVM.isSortByClinic
+ ? (groupedDoctors[index].first.clinicName ?? 'Unknown')
+ : (groupedDoctors[index].first.projectName ?? 'Unknown'),
+ style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600),
+ overflow: TextOverflow.ellipsis,
+ ),
+ // Expanded content - list of doctors in this group
+ AnimatedSwitcher(
+ duration: Duration(milliseconds: 400),
+ child: isExpanded
+ ? Container(
+ key: ValueKey(index),
+ padding: EdgeInsets.only(top: 12.h),
+ child: Column(
+ children: groupedDoctors[index].asMap().entries.map((entry) {
+ final doctorIndex = entry.key;
+ final doctor = entry.value;
+ final isLastDoctor = doctorIndex == groupedDoctors[index].length - 1;
+ return Column(
+ children: [
+ DoctorCard(
+ doctorsListResponseModel: doctor,
+ isLoading: false,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ ).onPress(() async {
+ bookAppointmentsVM.setSelectedDoctor(doctor);
+ LoaderBottomSheet.showLoader();
+ await bookAppointmentsVM.getDoctorProfile(
+ onSuccess: (dynamic respData) {
+ LoaderBottomSheet.hideLoader();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: DoctorProfilePage(),
+ ),
+ );
+ },
+ onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: err),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ },
+ );
+ }),
+ if (!isLastDoctor)
+ Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
+ ],
+ );
+ }).toList(),
+ ),
+ )
+ : SizedBox.shrink(),
+ ),
+ ],
+ ),
+ ),
+ ),
),
),
),
@@ -296,6 +384,7 @@ class _SearchDoctorByNameState extends State {
@override
void dispose() {
+ _scrollController.dispose();
bookAppointmentsViewModel.doctorsList.clear();
super.dispose();
}
diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart
index 569e8ac..2f8747d 100644
--- a/lib/presentation/book_appointment/select_doctor_page.dart
+++ b/lib/presentation/book_appointment/select_doctor_page.dart
@@ -33,7 +33,6 @@ class SelectDoctorPage extends StatefulWidget {
class _SelectDoctorPageState extends State {
TextEditingController searchEditingController = TextEditingController();
- int? expandedIndex;
FocusNode textFocusNode = FocusNode();
@@ -170,8 +169,7 @@ class _SelectDoctorPageState extends State {
),
],
).paddingSymmetrical(0.h, 0.h),
- if (bookAppointmentsViewModel.isGetDocForHealthCal && bookAppointmentsVM.showSortFilterButtons)
- SizedBox(height: 16.h),
+ if (bookAppointmentsViewModel.isGetDocForHealthCal && bookAppointmentsVM.showSortFilterButtons) SizedBox(height: 16.h),
Row(
mainAxisSize: MainAxisSize.max,
children: [
@@ -190,7 +188,6 @@ class _SelectDoctorPageState extends State {
value: bookAppointmentsVM.isNearestAppointmentSelected,
onChanged: (newValue) async {
bookAppointmentsVM.setIsNearestAppointmentSelected(newValue);
-
},
),
],
@@ -199,11 +196,10 @@ class _SelectDoctorPageState extends State {
padding: EdgeInsets.only(top: 16.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
- itemCount: bookAppointmentsVM.isDoctorsListLoading
- ? 5
- : (bookAppointmentsVM.doctorsListGrouped.isNotEmpty ? bookAppointmentsVM.doctorsListGrouped.length : 1),
+ itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : (bookAppointmentsVM.doctorsListGrouped.isNotEmpty ? bookAppointmentsVM.doctorsListGrouped.length : 1),
itemBuilder: (context, index) {
- final isExpanded = expandedIndex == index;
+ // final isExpanded = bookAppointmentsVM.expandedGroupIndex == index;
+ final isExpanded = true;
return bookAppointmentsVM.isDoctorsListLoading
? DoctorCard(
doctorsListResponseModel: DoctorsListResponseModel(),
@@ -225,13 +221,11 @@ class _SelectDoctorPageState extends State {
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: InkWell(
onTap: () {
- setState(() {
- expandedIndex = isExpanded ? null : index;
- });
+ bookAppointmentsVM.toggleGroupExpansion(index);
// After rebuild, ensure the expanded item is visible
WidgetsBinding.instance.addPostFrameCallback((_) {
final key = _itemKeys[index];
- if (key != null && key.currentContext != null && expandedIndex == index) {
+ if (key != null && key.currentContext != null && bookAppointmentsVM.expandedGroupIndex == index) {
Scrollable.ensureVisible(
key.currentContext!,
duration: Duration(milliseconds: 350),
@@ -282,34 +276,41 @@ class _SelectDoctorPageState extends State {
key: ValueKey(index),
padding: EdgeInsets.only(top: 12.h),
child: Column(
- children: bookAppointmentsVM.doctorsListGrouped[index].map((doctor) {
- return Container(
- margin: EdgeInsets.only(bottom: 12.h),
- child: DoctorCard(
- doctorsListResponseModel: doctor,
- isLoading: false,
- bookAppointmentsViewModel: bookAppointmentsViewModel,
- ).onPress(() async {
- bookAppointmentsVM.setSelectedDoctor(doctor);
- LoaderBottomSheet.showLoader();
- await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
- LoaderBottomSheet.hideLoader();
- Navigator.of(context).push(
- CustomPageRoute(
- page: DoctorProfilePage(),
- ),
- );
- }, onError: (err) {
- LoaderBottomSheet.hideLoader();
- showCommonBottomSheetWithoutHeight(
- context,
- child: Utils.getErrorWidget(loadingText: err),
- callBackFunc: () {},
- isFullScreen: false,
- isCloseButtonVisible: true,
- );
- });
- }),
+ children: bookAppointmentsVM.doctorsListGrouped[index].asMap().entries.map((entry) {
+ final doctorIndex = entry.key;
+ final doctor = entry.value;
+ final isLastDoctor = doctorIndex == bookAppointmentsVM.doctorsListGrouped[index].length - 1;
+
+ return Column(
+ children: [
+ DoctorCard(
+ doctorsListResponseModel: doctor,
+ isLoading: false,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ ).onPress(() async {
+ bookAppointmentsVM.setSelectedDoctor(doctor);
+ LoaderBottomSheet.showLoader();
+ await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
+ LoaderBottomSheet.hideLoader();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: DoctorProfilePage(),
+ ),
+ );
+ }, onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: err),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ });
+ }),
+ if (!isLastDoctor)
+ Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
+ ],
);
}).toList(),
),
diff --git a/lib/presentation/book_appointment/widgets/doctor_card.dart b/lib/presentation/book_appointment/widgets/doctor_card.dart
index ffe26ff..d2e1c0b 100644
--- a/lib/presentation/book_appointment/widgets/doctor_card.dart
+++ b/lib/presentation/book_appointment/widgets/doctor_card.dart
@@ -38,20 +38,49 @@ class DoctorCard extends StatelessWidget {
hasShadow: false,
),
child: Padding(
- padding: EdgeInsets.all(14.h),
+ padding: EdgeInsets.only(top: 14.h,bottom: 20.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Image.network(
- isLoading
- ? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png"
- : doctorsListResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png",
- width: 63.h,
- height: 63.h,
- fit: BoxFit.cover,
- ).circle(100).toShimmer2(isShow: isLoading),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Image.network(
+ isLoading
+ ? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png"
+ : doctorsListResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png",
+ width: 63.h,
+ height: 63.h,
+ fit: BoxFit.cover,
+ ).circle(100).toShimmer2(isShow: isLoading),
+ Transform.translate(
+ offset: Offset(0.0, -20.h),
+ child: Container(
+ width: 40.w,
+ height: 40.h,
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ shape: BoxShape.circle, // Makes the container circular
+ border: Border.all(
+ color: AppColors.scaffoldBgColor, // Color of the border
+ width: 1.5.w, // Width of the border
+ ),
+ ),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h),
+ SizedBox(height: 2.h),
+ "${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor),
+ ],
+ ),
+ ).circle(100).toShimmer2(isShow: isLoading),
+ ),
+ ],
+ ),
SizedBox(width: 8.h),
Expanded(
flex: 9,
@@ -70,15 +99,19 @@ class DoctorCard extends StatelessWidget {
),
SizedBox(height: 2.h),
Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- (isLoading
- ? "Consultant Cardiologist"
- : doctorsListResponseModel.speciality!.isNotEmpty
- ? doctorsListResponseModel.speciality!.first
- : "")
- .toString()
- .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 1)
- .toShimmer2(isShow: isLoading),
+ SizedBox(
+ width: MediaQuery.of(context).size.width * 0.45,
+ child: (isLoading
+ ? "Consultant Cardiologist"
+ : doctorsListResponseModel.speciality!.isNotEmpty
+ ? doctorsListResponseModel.speciality!.first
+ : "")
+ .toString()
+ .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 2)
+ .toShimmer2(isShow: isLoading),
+ ),
SizedBox(width: 6.w),
Image.network(
isLoading ? "https://hmgwebservices.com/Images/flag/SYR.png" : doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
@@ -98,23 +131,17 @@ class DoctorCard extends StatelessWidget {
),
],
),
- SizedBox(height: 12.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
- labelText: "Clinic: ${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}".needTranslation,
+ labelText: "${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}".needTranslation,
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
- labelText: "Branch: ${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}".needTranslation,
+ labelText: "${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}".needTranslation,
).toShimmer2(isShow: isLoading),
- doctorsListResponseModel.decimalDoctorRate != null ? AppCustomChipWidget(
- icon: AppAssets.rating_icon,
- iconColor: AppColors.ratingColorYellow,
- labelText: "Rating: ${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".needTranslation,
- ).toShimmer2(isShow: isLoading) : SizedBox(),
bookAppointmentsViewModel.isNearestAppointmentSelected
? doctorsListResponseModel.nearestFreeSlot != null
? AppCustomChipWidget(
diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart
index e49a621..5c028db 100644
--- a/lib/presentation/hmg_services/services_page.dart
+++ b/lib/presentation/hmg_services/services_page.dart
@@ -4,15 +4,18 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/route_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/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_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/hmg_services/models/ui_models/hmg_services_component_model.dart';
+import 'package:hmg_patient_app_new/features/hospital/hospital_selection_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
@@ -38,6 +41,7 @@ import 'package:provider/provider.dart';
import '../../core/dependencies.dart' show getIt;
import '../../features/qr_parking/qr_parking_view_model.dart';
+import '../emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart';
class ServicesPage extends StatelessWidget {
ServicesPage({super.key});
@@ -46,27 +50,24 @@ class ServicesPage extends StatelessWidget {
late MedicalFileViewModel medicalFileViewModel;
late final List hmgServices = [
- HmgServicesComponentModel(
- 11,
- "Emergency Services".needTranslation,
- "".needTranslation,
- AppAssets.emergency_services_icon,
- bgColor: AppColors.primaryRedColor,
- true,
- route: null, onTap: () {
- getIt.get().flushData();
- getIt.get().getTransportationOrders(
- showLoader: false,
- );
- getIt.get().getRRTOrders(
- showLoader: false,
- );
- Navigator.of(GetIt.instance().navigatorKey.currentContext!).push(
- CustomPageRoute(
- page: EmergencyServicesPage(),
- settings: const RouteSettings(name: '/EmergencyServicesPage'),
- ),
- );
+ HmgServicesComponentModel(11, "Emergency Services".needTranslation, "".needTranslation, AppAssets.emergency_services_icon, bgColor: AppColors.primaryRedColor, true, route: null, onTap: () async {
+ if (getIt.get().isAuthenticated) {
+ getIt.get().flushData();
+ getIt.get().getTransportationOrders(
+ showLoader: false,
+ );
+ getIt.get().getRRTOrders(
+ showLoader: false,
+ );
+ Navigator.of(GetIt.instance().navigatorKey.currentContext!).push(
+ CustomPageRoute(
+ page: EmergencyServicesPage(),
+ settings: const RouteSettings(name: '/EmergencyServicesPage'),
+ ),
+ );
+ } else {
+ await getIt.get().onLoginPressed();
+ }
}),
HmgServicesComponentModel(
11,
@@ -77,15 +78,13 @@ class ServicesPage extends StatelessWidget {
true,
route: AppRoutes.bookAppointmentPage,
),
- HmgServicesComponentModel(
- 5,
- "Complete Checkup".needTranslation,
- "".needTranslation,
- AppAssets.comprehensiveCheckup,
- bgColor: AppColors.bgGreenColor,
- true,
- route: AppRoutes.comprehensiveCheckupPage,
- ),
+ HmgServicesComponentModel(5, "Complete Checkup".needTranslation, "".needTranslation, AppAssets.comprehensiveCheckup, bgColor: AppColors.bgGreenColor, true, route: null, onTap: () async {
+ if (getIt.get().isAuthenticated) {
+ getIt.get().pushPageRoute(AppRoutes.comprehensiveCheckupPage);
+ } else {
+ await getIt.get().onLoginPressed();
+ }
+ }),
HmgServicesComponentModel(
11,
"Indoor Navigation".needTranslation,
@@ -94,17 +93,51 @@ class ServicesPage extends StatelessWidget {
bgColor: Color(0xff45A2F8),
true,
route: null,
- onTap: () {},
+ onTap: () {
+ showCommonBottomSheetWithoutHeight(
+ title: LocaleKeys.selectHospital.tr(),
+ GetIt.instance().navigatorKey.currentContext!,
+ 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(GetIt.instance().navigatorKey.currentContext!);
+ 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: () {
+ GetIt.instance().navigatorKey.currentContext!.read().clearSearchText();
+ },
+ );
+ },
),
HmgServicesComponentModel(
- 11,
- "E-Referral Services".needTranslation,
- "".needTranslation,
- AppAssets.eReferral,
- bgColor: AppColors.eReferralCardColor,
- true,
- route: AppRoutes.eReferralPage,
- ),
+ 11, "E-Referral Services".needTranslation, "".needTranslation, AppAssets.eReferral, bgColor: AppColors.eReferralCardColor, true, route: null, onTap: () async {
+ if (getIt.get().isAuthenticated) {
+ getIt.get().pushPageRoute(AppRoutes.eReferralPage);
+ } else {
+ await getIt.get().onLoginPressed();
+ }
+ }),
HmgServicesComponentModel(
3,
"Blood Donation".needTranslation,
@@ -182,7 +215,14 @@ class ServicesPage extends StatelessWidget {
AppAssets.general_health,
bgColor: AppColors.whiteColor,
true,
- route: AppRoutes.healthTrackersPage,
+ route: null,
+ onTap: () async {
+ if (getIt.get().isAuthenticated) {
+ getIt.get().pushPageRoute(AppRoutes.healthTrackersPage);
+ } else {
+ await getIt.get().onLoginPressed();
+ }
+ },
),
HmgServicesComponentModel(
11,
@@ -193,24 +233,28 @@ class ServicesPage extends StatelessWidget {
true,
route: null, // Set to null since we handle navigation in onTap
onTap: () async {
- LoaderBottomSheet.showLoader(loadingText: "Fetching your water intake details.".needTranslation);
- final waterMonitorVM = getIt.get();
- final context = getIt.get().navigatorKey.currentContext!;
- await waterMonitorVM.fetchUserDetailsForMonitoring(
- onSuccess: (userDetail) {
- LoaderBottomSheet.hideLoader();
- if (userDetail == null) {
- waterMonitorVM.populateFromAuthenticatedUser();
- context.navigateWithName(AppRoutes.waterMonitorSettingsPage);
- } else {
+ if (getIt.get().isAuthenticated) {
+ LoaderBottomSheet.showLoader(loadingText: "Fetching your water intake details.".needTranslation);
+ final waterMonitorVM = getIt.get();
+ final context = getIt.get().navigatorKey.currentContext!;
+ await waterMonitorVM.fetchUserDetailsForMonitoring(
+ onSuccess: (userDetail) {
+ LoaderBottomSheet.hideLoader();
+ if (userDetail == null) {
+ waterMonitorVM.populateFromAuthenticatedUser();
+ context.navigateWithName(AppRoutes.waterMonitorSettingsPage);
+ } else {
+ context.navigateWithName(AppRoutes.waterConsumptionPage);
+ }
+ },
+ onError: (error) {
+ LoaderBottomSheet.hideLoader();
context.navigateWithName(AppRoutes.waterConsumptionPage);
- }
- },
- onError: (error) {
- LoaderBottomSheet.hideLoader();
- context.navigateWithName(AppRoutes.waterConsumptionPage);
- },
- );
+ },
+ );
+ } else {
+ await getIt.get().onLoginPressed();
+ }
},
),
HmgServicesComponentModel(
@@ -238,7 +282,14 @@ class ServicesPage extends StatelessWidget {
AppAssets.smartwatch_icon,
bgColor: AppColors.whiteColor,
true,
- route: AppRoutes.smartWatches,
+ route: null,
+ onTap: () async {
+ if (getIt.get().isAuthenticated) {
+ getIt.get().pushPageRoute(AppRoutes.smartWatches);
+ } else {
+ await getIt.get().onLoginPressed();
+ }
+ },
// route: AppRoutes.huaweiHealthExample,
),
];
@@ -331,14 +382,17 @@ class ServicesPage extends StatelessWidget {
],
),
Spacer(),
- Consumer(builder: (context, habibWalletVM, child) {
- return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, isExpanded: false)
- .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h);
- }),
+ getIt.get().isAuthenticated
+ ? Consumer(builder: (context, habibWalletVM, child) {
+ return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, isExpanded: false)
+ .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h);
+ })
+ : "Login to view your wallet balance".needTranslation.toText12(fontWeight: FontWeight.w500, maxLine: 2),
Spacer(),
- CustomButton(
- height: 40.h,
- icon: AppAssets.recharge_icon,
+ getIt.get().isAuthenticated
+ ? CustomButton(
+ height: 40.h,
+ icon: AppAssets.recharge_icon,
iconSize: 16.w,
iconColor: AppColors.infoColor,
textColor: AppColors.infoColor,
@@ -352,10 +406,15 @@ class ServicesPage extends StatelessWidget {
onPressed: () {
Navigator.of(context).push(CustomPageRoute(page: RechargeWalletPage()));
},
- ),
+ )
+ : SizedBox.shrink(),
],
- ).onPress(() {
- Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage()));
+ ).onPress(() async {
+ if (getIt.get().isAuthenticated) {
+ Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage()));
+ } else {
+ await getIt.get().onLoginPressed();
+ }
}),
),
),
@@ -377,13 +436,14 @@ class ServicesPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.services_medical_file_icon, width: 30.w, height: 30.h),
- "Medical Files".needTranslation.toText14(weight: FontWeight.w600, maxlines: 2).expanded,
+ LocaleKeys.medicalFile.tr().toText14(weight: FontWeight.w600, maxlines: 2).expanded,
Utils.buildSvgWithAssets(icon: AppAssets.arrow_forward),
],
),
Spacer(),
- Wrap(
- spacing: -8.h,
+ getIt.get().isAuthenticated
+ ? Wrap(
+ spacing: -8.h,
// runSpacing: 0.h,
children: [
Utils.buildImgWithAssets(
@@ -411,10 +471,12 @@ class ServicesPage extends StatelessWidget {
fit: BoxFit.contain,
),
],
- ),
+ )
+ : "Login to view your medical file".needTranslation.toText12(fontWeight: FontWeight.w500, maxLine: 2),
Spacer(),
- CustomButton(
- height: 40.h,
+ getIt.get().isAuthenticated
+ ? CustomButton(
+ height: 40.h,
icon: AppAssets.add_icon,
iconSize: 16.w,
iconColor: AppColors.primaryRedColor,
@@ -436,14 +498,19 @@ class ServicesPage extends StatelessWidget {
medicalFileViewModel.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms);
});
},
- ),
+ )
+ : SizedBox.shrink(),
],
- ).onPress(() {
- Navigator.of(context).push(
- CustomPageRoute(
- page: MedicalFilePage(),
- ),
- );
+ ).onPress(() async {
+ if (getIt.get().isAuthenticated) {
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: MedicalFilePage(),
+ ),
+ );
+ } else {
+ await getIt.get().onLoginPressed();
+ }
}),
),
),
@@ -539,7 +606,6 @@ class ServicesPage extends StatelessWidget {
),
),
);
-
}),
),
),
diff --git a/lib/presentation/home/data/landing_page_data.dart b/lib/presentation/home/data/landing_page_data.dart
index c693479..ef2832b 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 6d7a91c..789bc4d 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';
@@ -61,6 +64,8 @@ import 'package:provider/provider.dart';
import '../active_medication/active_medication_page.dart';
+import '../emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart';
+
class LandingPage extends StatefulWidget {
const LandingPage({super.key});
@@ -82,6 +87,7 @@ class _LandingPageState extends State {
late BookAppointmentsViewModel bookAppointmentsViewModel;
late EmergencyServicesViewModel emergencyServicesViewModel;
late TodoSectionViewModel todoSectionViewModel;
+ late NotificationsViewModel notificationsViewModel;
final SwiperController _controller = SwiperController();
@@ -108,22 +114,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();
@@ -137,6 +147,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,
@@ -182,37 +193,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
@@ -312,15 +351,49 @@ class _LandingPageState extends State {
isFromHomePage: true,
),
).paddingSymmetrical(24.h, 0.h)
- : Swiper(
- itemCount: myAppointmentsVM.isMyAppointmentsLoading
- ? 3
- : myAppointmentsVM.patientAppointmentsHistoryList.length < 3
- ? myAppointmentsVM.patientAppointmentsHistoryList.length
- : 3,
- layout: SwiperLayout.STACK,
- loop: true,
- itemWidth: MediaQuery.of(context).size.width - 48.h,
+ : isTablet
+ ? SizedBox(
+ height: isFoldable ? 290.h : 255.h,
+ child: ListView.separated(
+ scrollDirection: Axis.horizontal,
+ itemCount: 3,
+ shrinkWrap: true,
+ padding: EdgeInsets.only(left: 16.h, right: 16.h),
+ itemBuilder: (context, index) {
+ return SizedBox(
+ height: 255.h,
+ width: 250.w,
+ child: getIndexSwiperCard(index),
+ );
+ // return AnimationConfiguration.staggeredList(
+ // position: index,
+ // duration: const Duration(milliseconds: 1000),
+ // child: SlideAnimation(
+ // horizontalOffset: 100.0,
+ // child: FadeInAnimation(
+ // child: SizedBox(
+ // height: 255.h,
+ // width: 250.w,
+ // child: getIndexSwiperCard(index),
+ // ),
+ // ),
+ // ),
+ // );
+ },
+ separatorBuilder: (BuildContext cxt, int index) => SizedBox(
+ width: 10.w,
+ ),
+ ),
+ )
+ : Swiper(
+ itemCount: myAppointmentsVM.isMyAppointmentsLoading
+ ? 3
+ : myAppointmentsVM.patientAppointmentsHistoryList.length < 3
+ ? myAppointmentsVM.patientAppointmentsHistoryList.length
+ : 3,
+ layout: SwiperLayout.STACK,
+ loop: true,
+ itemWidth: MediaQuery.of(context).size.width - 48.h,
indicatorLayout: PageIndicatorLayout.COLOR,
axisDirection: AxisDirection.right,
controller: _controller,
@@ -331,208 +404,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(
@@ -773,6 +645,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,
@@ -831,4 +985,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 6216880..49db8bf 100644
--- a/lib/presentation/home/widgets/small_service_card.dart
+++ b/lib/presentation/home/widgets/small_service_card.dart
@@ -1,17 +1,22 @@
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';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hospital/hospital_selection_view_model.dart';
+import 'package:hmg_patient_app_new/features/qr_parking/qr_parking_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';
+import 'package:hmg_patient_app_new/presentation/parking/paking_page.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
@@ -126,6 +131,39 @@ 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;
+ case "parking_guide":
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (_) => ChangeNotifierProvider(
+ create: (_) => getIt(),
+ child: const ParkingPage(),
+ ),
+ ),
+ );
+ 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 0000000..99d4270
--- /dev/null
+++ b/lib/presentation/notifications/notifications_list_page.dart
@@ -0,0 +1,78 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
+import 'package:hmg_patient_app_new/core/utils/date_util.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),
+ Row(
+ children: [
+ Expanded(child: notificationsVM.notificationsList[index].message!.toText16(isBold: notificationsVM.notificationsList[index].isRead ?? false)),
+ ],
+ ),
+ SizedBox(height: 12.h),
+ DateUtil.formatDateToDate(DateUtil.convertStringToDate(notificationsVM.notificationsList[index].isSentOn!), false).toText14(weight: FontWeight.w500),
+ 1.divider,
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }).paddingSymmetrical(16.w, 0.h),
+ ).paddingSymmetrical(24.w, 0.h);
+ }),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/parking/paking_page.dart b/lib/presentation/parking/paking_page.dart
index cd1e8bc..a82703b 100644
--- a/lib/presentation/parking/paking_page.dart
+++ b/lib/presentation/parking/paking_page.dart
@@ -55,7 +55,7 @@ class _ParkingPageState extends State {
children: [
Expanded(
child: SingleChildScrollView(
- padding: const EdgeInsets.symmetric(horizontal: 16),
+ padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart
index e463ae7..62f090e 100644
--- a/lib/presentation/profile_settings/profile_settings.dart
+++ b/lib/presentation/profile_settings/profile_settings.dart
@@ -217,12 +217,12 @@ class ProfileSettingsState extends State {
child: Column(
children: [
actionItem(AppAssets.email_transparent, "Update Email Address".needTranslation, () {}),
- 1.divider,
- actionItem(AppAssets.smart_phone_fill, "Phone Number".needTranslation, () {}),
- 1.divider,
- actionItem(AppAssets.my_address, "My Addresses".needTranslation, () {}),
- 1.divider,
- actionItem(AppAssets.emergency, "Emergency Contact".needTranslation, () {}),
+ // 1.divider,
+ // actionItem(AppAssets.smart_phone_fill, "Phone Number".needTranslation, () {}),
+ // 1.divider,
+ // actionItem(AppAssets.my_address, "My Addresses".needTranslation, () {}),
+ // 1.divider,
+ // actionItem(AppAssets.emergency, "Emergency Contact".needTranslation, () {}),
],
),
),
diff --git a/lib/presentation/todo_section/ancillary_order_payment_page.dart b/lib/presentation/todo_section/ancillary_order_payment_page.dart
index fdfcc04..054108d 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/routes/app_routes.dart b/lib/routes/app_routes.dart
index d183d02..14bacdc 100644
--- a/lib/routes/app_routes.dart
+++ b/lib/routes/app_routes.dart
@@ -133,21 +133,17 @@ class AppRoutes {
return HealthTrackerDetailPage(
trackerType: args ?? HealthTrackerTypeEnum.bloodSugar,
);
- },
- monthlyReports: (context) =>
- ChangeNotifierProvider(
- create: (_) =>
- MonthlyReportsViewModel(
- monthlyReportsRepo: getIt(),
- errorHandlerService: getIt(),
- ),
- child: const MonthlyReportsPage(),
- ),
-
- qrParking: (context) => ChangeNotifierProvider(
- create: (_) => getIt(),
- child: const ParkingPage(),
- )
- };
+ // monthlyReports: (context) => ChangeNotifierProvider(
+ // create: (_) => MonthlyReportsViewModel(
+ // monthlyReportsRepo: getIt(),
+ // errorHandlerService: getIt(),
+ // ),
+ // child: const MonthlyReportsPage(),
+ // ),
+ // qrParking: (context) => ChangeNotifierProvider(
+ // create: (_) => getIt(),
+ // child: const ParkingPage(),
+ },
+ };
}
diff --git a/lib/widgets/appbar/app_bar_widget.dart b/lib/widgets/appbar/app_bar_widget.dart
index b3bc3eb..942a93f 100644
--- a/lib/widgets/appbar/app_bar_widget.dart
+++ b/lib/widgets/appbar/app_bar_widget.dart
@@ -42,15 +42,15 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
? RotatedBox(
quarterTurns: 90,
child: Utils.buildSvgWithAssets(
- icon: AppAssets.arrow_back,
- width: 32.w,
- height: 32.h,
+ icon: AppAssets.forward_top_nav_icon,
+ width: 24.w,
+ height: 24.h,
),
)
: Utils.buildSvgWithAssets(
- icon: AppAssets.arrow_back,
- width: 32.w,
- height: 32.h,
+ icon: AppAssets.forward_top_nav_icon,
+ width: 24.w,
+ height: 24.h,
),
),
),
diff --git a/lib/widgets/appbar/collapsing_list_view.dart b/lib/widgets/appbar/collapsing_list_view.dart
index 5409fcf..0580776 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) {