# Conflicts:
#	lib/core/dependencies.dart
#	lib/routes/app_routes.dart
dev_sultan
Sultan khan 1 month ago
commit 8b5c4d7575

@ -160,6 +160,7 @@ dependencies {
annotationProcessor("com.github.bumptech.glide:compiler:4.16.0") annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
implementation("com.mapbox.maps:android:11.5.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") // implementation("com.mapbox.maps:android:11.4.0")
// AARs // AARs

@ -21,7 +21,7 @@ class PenguinNavigator() {
val postToken = PostToken(clientID, clientKey) val postToken = PostToken(clientID, clientKey)
getToken(mContext, postToken, object : RefIdDelegate { getToken(mContext, postToken, object : RefIdDelegate {
override fun onRefByIDSuccess(PoiId: String?) { override fun onRefByIDSuccess(PoiId: String?) {
Log.e("navigateTo", "PoiId is+++++++ $PoiId") Log.e("navigateTo", "PoiId is+++++++ $refID")
PlugAndPlaySDK.navigateTo(mContext, refID, object : RefIdDelegate { PlugAndPlaySDK.navigateTo(mContext, refID, object : RefIdDelegate {
override fun onRefByIDSuccess(PoiId: String?) { override fun onRefByIDSuccess(PoiId: String?) {

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.98978 14.0362H21.2589C22.0592 14.0362 22.7076 13.3877 22.7076 12.5874C22.7076 11.7871 22.0592 11.1387 21.2589 11.1387H1.98978C1.18949 11.1387 0.541029 11.7871 0.541029 12.5874C0.541029 13.3877 1.18949 14.0362 1.98978 14.0362Z" fill="#2E3039"/>
<path d="M14.6241 20.8879C14.9764 20.8879 15.3299 20.7598 15.6087 20.5013L22.9973 13.6488C23.2952 13.3723 23.4638 12.9835 23.4609 12.5767C23.458 12.1704 23.2847 11.7833 22.9828 11.511L15.3914 4.65838C14.7974 4.12234 13.8812 4.16928 13.3451 4.76327C12.8091 5.35726 12.856 6.27345 13.45 6.80949L19.8663 12.601L13.6384 18.3769C13.0519 18.921 13.0171 19.8378 13.5613 20.4243C13.847 20.732 14.2347 20.8879 14.6241 20.8879Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 803 B

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22.0102 14.0362H2.74111C1.94082 14.0362 1.29236 13.3877 1.29236 12.5874C1.29236 11.7871 1.94082 11.1387 2.74111 11.1387H22.0102C22.8105 11.1387 23.459 11.7871 23.459 12.5874C23.459 13.3877 22.8105 14.0362 22.0102 14.0362Z" fill="#2E3039"/>
<path d="M9.3759 20.8879C9.02357 20.8879 8.67007 20.7598 8.39133 20.5013L1.0027 13.6488C0.704836 13.3723 0.536202 12.9835 0.539099 12.5767C0.541997 12.1704 0.715267 11.7833 1.01719 11.511L8.60864 4.65838C9.20263 4.12234 10.1188 4.16928 10.6549 4.76327C11.1909 5.35726 11.144 6.27345 10.55 6.80949L4.13374 12.601L10.3616 18.3769C10.9481 18.921 10.9829 19.8378 10.4387 20.4243C10.153 20.732 9.76533 20.8879 9.3759 20.8879Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 800 B

@ -14,7 +14,7 @@ class PenguinNavigator {
} }
func navigateToPOI( referenceId:String,completion: @escaping (Bool, String?) -> Void) { 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 { if let error = error {
let errorMessage = "Token error while getting the for Navigate to method" let errorMessage = "Token error while getting the for Navigate to method"

@ -312,6 +312,8 @@ class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitia
print("====== after eventsDelegate onPenNavSuccess =========") 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 // self?.handleNavigation(clinicID: clinicID, token: token) { success, errorMessage in
//
if success { // if success {
//
print("Navigation successful") // print("Navigation successful")
//
} else { // } else {
//
print("Navigation failed: \(errorMessage ?? "Unknown error")") // print("Navigation failed: \(errorMessage ?? "Unknown error")")
//
} // }
//
//
//
} // }

@ -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 GET_PATIENT_LAST_RECORD = 'Services/Patients.svc/REST/Med_GetPatientLastRecord';
var INSERT_PATIENT_HEALTH_DATA = 'Services/Patients.svc/REST/Med_InsertTransactions'; 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 ADD_ACTIVE_PRESCRIPTIONS_REPORT_BY_PATIENT_ID = 'Services/Patients.svc/Rest/GetActivePrescriptionReportByPatientID';
var GET_CALL_INFO_HOURS_RESULT = 'Services/Doctors.svc/REST/GetCallInfoHoursResult'; 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'; var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatbot_SendMedicationInstructionByWhatsApp';
const DASHBOARD = 'Services/Patients.svc/REST/PatientDashboard';
class ApiConsts { class ApiConsts {
static const maxSmallScreen = 660; static const maxSmallScreen = 660;

@ -225,6 +225,9 @@ class AppAssets {
static const String trade_down_red = '$svgBasePath/trade_down_red.svg'; static const String trade_down_red = '$svgBasePath/trade_down_red.svg';
static const String pharmacy_icon = '$svgBasePath/phramacy_icon.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// //bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg'; static const String homeBottom = '$svgBasePath/home_bottom.svg';
static const String bookAppoBottom = '$svgBasePath/book_appo_bottom.svg'; static const String bookAppoBottom = '$svgBasePath/book_appo_bottom.svg';

@ -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_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_repo.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_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_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_repo.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart'; import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart';
@ -163,13 +165,14 @@ class AppDependencies {
),); ),);
getIt.registerLazySingleton<MonthlyReportsRepo>(() => MonthlyReportsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<MonthlyReportsRepo>(() => MonthlyReportsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<QrParkingRepo>(() => QrParkingRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<QrParkingRepo>(() => QrParkingRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
// getIt.registerFactory<QrParkingViewModel>( getIt.registerFactory<QrParkingViewModel>(
// () => QrParkingViewModel( () => QrParkingViewModel(
// qrParkingRepo: getIt<QrParkingRepo>(), qrParkingRepo: getIt<QrParkingRepo>(),
// errorHandlerService: getIt<ErrorHandlerService>(), errorHandlerService: getIt<ErrorHandlerService>(),
// cacheService: getIt<CacheService>(), cacheService: getIt<CacheService>(),
// ), ),
// ); );
getIt.registerLazySingleton<NotificationsRepo>(() => NotificationsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
// ViewModels // ViewModels
// Global/shared VMs LazySingleton // Global/shared VMs LazySingleton
@ -289,14 +292,12 @@ class AppDependencies {
getIt.registerLazySingleton<WaterMonitorViewModel>(() => WaterMonitorViewModel(waterMonitorRepo: getIt(), errorHandlerService: getIt())); getIt.registerLazySingleton<WaterMonitorViewModel>(() => WaterMonitorViewModel(waterMonitorRepo: getIt(), errorHandlerService: getIt()));
//commenting this because its already define there was on run time error because of this. //commenting this because its already define there was on run time error because of this.
// getIt.registerLazySingleton<MyInvoicesViewModel>(() => MyInvoicesViewModel(myInvoicesRepo: getIt(), errorHandlerService: getIt(), navServices: getIt())); getIt.registerLazySingleton<MyInvoicesViewModel>(() => MyInvoicesViewModel(myInvoicesRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton<MonthlyReportViewModel>(() => MonthlyReportViewModel(errorHandlerService: getIt(), monthlyReportRepo: getIt())); getIt.registerLazySingleton<MonthlyReportViewModel>(() => MonthlyReportViewModel(errorHandlerService: getIt(), monthlyReportRepo: getIt()));
getIt.registerLazySingleton<MyInvoicesViewModel>(() => MyInvoicesViewModel(
myInvoicesRepo: getIt(), getIt.registerLazySingleton<NotificationsViewModel>(() => NotificationsViewModel(notificationsRepo: getIt(), errorHandlerService: getIt()));
errorHandlerService: getIt(),
navServices: getIt(),
));
getIt.registerLazySingleton<HealthTrackersViewModel>(() => HealthTrackersViewModel(healthTrackersRepo: getIt(), errorHandlerService: getIt())); getIt.registerLazySingleton<HealthTrackersViewModel>(() => HealthTrackersViewModel(healthTrackersRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<ActivePrescriptionsViewModel>( getIt.registerLazySingleton<ActivePrescriptionsViewModel>(
() => ActivePrescriptionsViewModel( () => ActivePrescriptionsViewModel(
@ -304,13 +305,5 @@ class AppDependencies {
activePrescriptionsRepo: getIt() activePrescriptionsRepo: getIt()
), ),
); );
getIt.registerFactory<QrParkingViewModel>(
() => QrParkingViewModel(
qrParkingRepo: getIt<QrParkingRepo>(),
errorHandlerService: getIt<ErrorHandlerService>(),
cacheService: getIt<CacheService>(),
),
);
} }
} }

@ -43,7 +43,7 @@ class Utils {
{ {
"Desciption": "Sahafa Hospital", "Desciption": "Sahafa Hospital",
"DesciptionN": "مستشفى الصحافة", "DesciptionN": "مستشفى الصحافة",
"ID": 1, "ID": 1, // Campus ID
"LegalName": "Sahafa Hospital", "LegalName": "Sahafa Hospital",
"LegalNameN": "مستشفى الصحافة", "LegalNameN": "مستشفى الصحافة",
"Name": "Sahafa Hospital", "Name": "Sahafa Hospital",
@ -55,19 +55,20 @@ class Utils {
"IsActive": true, "IsActive": true,
"IsHmg": true, "IsHmg": true,
"IsVidaPlus": false, "IsVidaPlus": false,
"Latitude": "24.8113774", "Latitude": "24.8111548",
"Longitude": "46.6239813", "Longitude": "46.6217912",
"MainProjectID": 130, "MainProjectID": 130,
"ProjectOutSA": false, "ProjectOutSA": false,
"UsingInDoctorApp": false "UsingInDoctorApp": false,
"IsHMC": false
},{ },{
"Desciption": "Jeddah Hospital", "Desciption": "Jeddah Fayhaa Hospital",
"DesciptionN": "مستشفى الصحافة", "DesciptionN": "مستشفى جدة الفيحاء",
"ID": 3, "ID": 3, // Campus ID
"LegalName": "Jeddah Hospital", "LegalName": "Jeddah Fayhaa Hospital",
"LegalNameN": "مستشفى الصحافة", "LegalNameN": "مستشفى جدة الفيحاء",
"Name": "Jeddah Hospital", "Name": "Jeddah Fayhaa Hospital",
"NameN": "مستشفى الصحافة", "NameN": "مستشفى جدة الفيحاء",
"PhoneNumber": "+966115222222", "PhoneNumber": "+966115222222",
"SetupID": "013311", "SetupID": "013311",
"DistanceInKilometers": 0, "DistanceInKilometers": 0,
@ -75,11 +76,12 @@ class Utils {
"IsActive": true, "IsActive": true,
"IsHmg": true, "IsHmg": true,
"IsVidaPlus": false, "IsVidaPlus": false,
"Latitude": "24.8113774", "Latitude": "21.5086703",
"Longitude": "46.6239813", "Longitude": "39.2121855",
"MainProjectID": 130, "MainProjectID": 130,
"ProjectOutSA": false, "ProjectOutSA": false,
"UsingInDoctorApp": false "UsingInDoctorApp": false,
"IsHMC": false
} }
]; ];

@ -246,7 +246,6 @@ class AuthenticationViewModel extends ChangeNotifier {
} }
Future<void> _handleNewImeiRegistration() async { Future<void> _handleNewImeiRegistration() async {
await selectDeviceImei(onSuccess: (dynamic respData) async { await selectDeviceImei(onSuccess: (dynamic respData) async {
try { try {
if (respData != null) { 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); bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true || _appState.getUserRegistrationPayload.patientOutSa == 1);
MyAppointmentsViewModel myAppointmentsVM = getIt<MyAppointmentsViewModel>(); MyAppointmentsViewModel myAppointmentsVM = getIt<MyAppointmentsViewModel>();
if (isSwitchUser && _appState.getSuperUserID == null) {
nationalIdController.text = responseID.toString();
}else if( isSwitchUser && _appState.getSuperUserID != null){
nationalIdController.text = _appState.getSuperUserID.toString();
}
final request = RequestUtils.getCommonRequestWelcome( final request = RequestUtils.getCommonRequestWelcome(
phoneNumber: phoneNumberController.text, phoneNumber: phoneNumberController.text,
otpTypeEnum: otpTypeEnum, otpTypeEnum: otpTypeEnum,
@ -761,14 +766,14 @@ class AuthenticationViewModel extends ChangeNotifier {
phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0") phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "") ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
: _appState.getAuthenticatedUser()!.mobileNumber)!; : _appState.getAuthenticatedUser()!.mobileNumber)!;
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!; nationalIdController.text = _appState.getAuthenticatedUser()!.patientIdentificationNo!;
onSuccess(); onSuccess();
} else if ((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) && } else if ((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) &&
_appState.getAuthenticatedUser() != null) { _appState.getAuthenticatedUser() != null) {
phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0") phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "") ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
: _appState.getAuthenticatedUser()!.mobileNumber)!; : _appState.getAuthenticatedUser()!.mobileNumber)!;
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!; nationalIdController.text = _appState.getAuthenticatedUser()!.patientIdentificationNo!;
onSuccess(); onSuccess();
} }
} }

@ -53,6 +53,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
int? calculationID = 0; int? calculationID = 0;
bool isSortByClinic = true; bool isSortByClinic = true;
// Accordion expansion state
int? expandedGroupIndex;
int initialSlotDuration = 0; int initialSlotDuration = 0;
bool isNearestAppointmentSelected = false; bool isNearestAppointmentSelected = false;
@ -158,13 +161,28 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isBodyPartsLoading = false; bool isBodyPartsLoading = false;
int duration = 0; int duration = 0;
setIsSortByClinic(bool value) { setIsSortByClinic(bool value) {
isSortByClinic = value; isSortByClinic = value;
doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital; doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
notifyListeners(); 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 // Sort filtered doctor list by clinic or hospital
void sortFilteredDoctorList(bool sortByClinic) { void sortFilteredDoctorList(bool sortByClinic) {
isSortByClinic = sortByClinic; isSortByClinic = sortByClinic;
@ -186,6 +204,22 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
// Group filtered doctors list for accordion display
List<List<DoctorsListResponseModel>> getGroupedFilteredDoctorsList() {
final clinicMap = <String, List<DoctorsListResponseModel>>{};
final hospitalMap = <String, List<DoctorsListResponseModel>>{};
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 // Group doctors by clinic and hospital
void _groupDoctorsList() { void _groupDoctorsList() {
final clinicMap = <String, List<DoctorsListResponseModel>>{}; final clinicMap = <String, List<DoctorsListResponseModel>>{};
@ -237,6 +271,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
isDoctorProfileLoading = true; isDoctorProfileLoading = true;
isLiveCareSchedule = false; isLiveCareSchedule = false;
currentlySelectedHospitalFromRegionFlow = null; currentlySelectedHospitalFromRegionFlow = null;
expandedGroupIndex = null; // Reset accordion state
clinicsList.clear(); clinicsList.clear();
doctorsList.clear(); doctorsList.clear();
liveCareClinicsList.clear(); liveCareClinicsList.clear();
@ -254,9 +289,24 @@ class BookAppointmentsViewModel extends ChangeNotifier {
this.isNearestAppointmentSelected = isNearestAppointmentSelected; this.isNearestAppointmentSelected = isNearestAppointmentSelected;
if (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 { } 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(); notifyListeners();

@ -40,6 +40,8 @@ class HmgServicesViewModel extends ChangeNotifier {
bool isHospitalListLoading = false; bool isHospitalListLoading = false;
bool isVitalSignLoading = false; bool isVitalSignLoading = false;
bool hasVitalSignDataLoaded = false;
// HHC specific loading states // HHC specific loading states
bool isHhcOrdersLoading = false; bool isHhcOrdersLoading = false;
bool isHhcServicesLoading = false; bool isHhcServicesLoading = false;
@ -878,6 +880,7 @@ class HmgServicesViewModel extends ChangeNotifier {
Function(dynamic)? onSuccess, Function(dynamic)? onSuccess,
Function(String)? onError, Function(String)? onError,
}) async { }) async {
if (hasVitalSignDataLoaded) return;
isVitalSignLoading = true; isVitalSignLoading = true;
notifyListeners(); notifyListeners();
@ -896,6 +899,7 @@ class HmgServicesViewModel extends ChangeNotifier {
isVitalSignLoading = false; isVitalSignLoading = false;
if (apiResponse.messageStatus == 1) { if (apiResponse.messageStatus == 1) {
vitalSignList = apiResponse.data ?? []; vitalSignList = apiResponse.data ?? [];
hasVitalSignDataLoaded = true;
notifyListeners(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);

@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.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/penguin_method_channel.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/hospital/AppPermission.dart'; import 'package:hmg_patient_app_new/features/hospital/AppPermission.dart';
@ -17,20 +19,32 @@ class HospitalSelectionBottomSheetViewModel extends ChangeNotifier {
int hmgCount = 0; int hmgCount = 0;
TextEditingController searchController = TextEditingController(); TextEditingController searchController = TextEditingController();
final AppState appState; final AppState appState;
LocationUtils locationUtils = getIt.get<LocationUtils>();
HospitalSelectionBottomSheetViewModel(this.appState) { HospitalSelectionBottomSheetViewModel(this.appState) {
Utils.navigationProjectsList.forEach((element) { locationUtils.getCurrentLocation(onSuccess: (value) {
HospitalsModel model = HospitalsModel.fromJson(element); Utils.navigationProjectsList.forEach((element) {
if (model.isHMC == true) { HospitalsModel model = HospitalsModel.fromJson(element);
hmcHospitalList.add(model);
} else { double dist = Utils.distance(value.latitude, value.longitude, double.parse(model.latitude!), double.parse(model.longitude!)).ceilToDouble();
hmgHospitalList.add(model); print(dist);
} model.distanceInKilometers = dist;
listOfData.add(model);
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() { getDisplayList() {
@ -79,13 +93,14 @@ class HospitalSelectionBottomSheetViewModel extends ChangeNotifier {
} }
void openPenguin(HospitalsModel hospital) { void openPenguin(HospitalsModel hospital, {String clinicID = ""}) {
initPenguinSDK(hospital.iD); initPenguinSDK(hospital.iD, clinicID: clinicID);
} }
initPenguinSDK(int projectID) async { initPenguinSDK(int projectID, {String clinicID = ""}) async {
NavigationClinicDetails data = NavigationClinicDetails(); NavigationClinicDetails data = NavigationClinicDetails();
data.projectId = projectID.toString(); data.projectId = projectID.toString();
data.clinicId = clinicID;
final bool permited = await AppPermission.askPenguinPermissions(); final bool permited = await AppPermission.askPenguinPermissions();
if (!permited) { if (!permited) {
Map<Permission, PermissionStatus> statuses = await [ Map<Permission, PermissionStatus> statuses = await [

@ -726,6 +726,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
} }
Future<void> getPatientAppointmentQueueDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> 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; isAppointmentQueueDetailsLoading = true;
notifyListeners(); notifyListeners();
final result = await myAppointmentsRepo.getPatientAppointmentQueueDetails( final result = await myAppointmentsRepo.getPatientAppointmentQueueDetails(

@ -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<String, dynamic> 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<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
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;
}
}

@ -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<Either<Failure, GenericApiModel<List<NotificationResponseModel>>>> 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<Either<Failure, GenericApiModel<List<NotificationResponseModel>>>> getAllNotifications({
required int notificationStatusID,
required int pagingSize,
required int currentPage,
}) async {
Map<String, dynamic> mapDevice = {
"NotificationStatusID": notificationStatusID,
"pagingSize": pagingSize,
"currentPage": currentPage,
};
try {
GenericApiModel<List<NotificationResponseModel>>? 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<List<NotificationResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: [],
);
return;
}
final notifications = list.map((item) => NotificationResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<NotificationResponseModel>();
apiResponse = GenericApiModel<List<NotificationResponseModel>>(
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()));
}
}
}

@ -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<NotificationResponseModel> 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<void> 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<void> refreshNotifications({
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
notificationsList.clear();
currentPage = 0;
hasMoreNotifications = true;
await getAllNotifications(onSuccess: onSuccess, onError: onError);
}
Future<void> loadMoreNotifications({
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
await getAllNotifications(onSuccess: onSuccess, onError: onError);
}
@override
void dispose() {
super.dispose();
}
}

@ -48,6 +48,8 @@ abstract class TodoSectionRepo {
}); });
Future<Either<Failure, dynamic>> applePayInsertRequest({required dynamic applePayInsertRequest}); Future<Either<Failure, dynamic>> applePayInsertRequest({required dynamic applePayInsertRequest});
Future<Either<Failure, dynamic>> getPatientDashboard();
} }
class TodoSectionRepoImp implements TodoSectionRepo { class TodoSectionRepoImp implements TodoSectionRepo {
@ -376,4 +378,29 @@ class TodoSectionRepoImp implements TodoSectionRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, dynamic>> getPatientDashboard() async {
Map<String, dynamic> 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()));
}
}
} }

@ -10,11 +10,15 @@ class TodoSectionViewModel extends ChangeNotifier {
TodoSectionViewModel({required this.todoSectionRepo, required this.errorHandlerService}); TodoSectionViewModel({required this.todoSectionRepo, required this.errorHandlerService});
String? notificationsCount = "0";
initializeTodoSectionViewModel() async { initializeTodoSectionViewModel() async {
patientAncillaryOrdersList.clear(); patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true; isAncillaryOrdersLoading = true;
isAncillaryDetailsProceduresLoading = true; isAncillaryDetailsProceduresLoading = true;
await getPatientOnlineAncillaryOrderList(); notificationsCount = "0";
getPatientOnlineAncillaryOrderList();
getPatientDashboard();
} }
bool isAncillaryOrdersLoading = false; bool isAncillaryOrdersLoading = false;
@ -28,6 +32,28 @@ class TodoSectionViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
Future<void> 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<void> getPatientOnlineAncillaryOrderList({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientOnlineAncillaryOrderList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientAncillaryOrdersList.clear(); patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true; isAncillaryOrdersLoading = true;

@ -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/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_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/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/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_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'; import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
@ -184,6 +185,9 @@ void main() async {
ChangeNotifierProvider<MonthlyReportViewModel>( ChangeNotifierProvider<MonthlyReportViewModel>(
create: (_) => getIt.get<MonthlyReportViewModel>(), create: (_) => getIt.get<MonthlyReportViewModel>(),
), ),
ChangeNotifierProvider<NotificationsViewModel>(
create: (_) => getIt.get<NotificationsViewModel>(),
),
ChangeNotifierProvider<ActivePrescriptionsViewModel>( ChangeNotifierProvider<ActivePrescriptionsViewModel>(
create: (_) => getIt.get<ActivePrescriptionsViewModel>(), create: (_) => getIt.get<ActivePrescriptionsViewModel>(),
), ),

@ -34,6 +34,14 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
TextEditingController searchEditingController = TextEditingController(); TextEditingController searchEditingController = TextEditingController();
FocusNode textFocusNode = FocusNode(); FocusNode textFocusNode = FocusNode();
late BookAppointmentsViewModel bookAppointmentsViewModel; late BookAppointmentsViewModel bookAppointmentsViewModel;
late ScrollController _scrollController;
final Map<int, GlobalKey> _itemKeys = {};
@override
void initState() {
_scrollController = ScrollController();
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -173,8 +181,11 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
padding: EdgeInsets.only(top: 20.h), padding: EdgeInsets.only(top: 20.h),
shrinkWrap: true, shrinkWrap: true,
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : bookAppointmentsVM.filteredDoctorList.length, itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : bookAppointmentsVM.getGroupedFilteredDoctorsList().length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final isExpanded = bookAppointmentsVM.expandedGroupIndex == index;
final groupedDoctors = bookAppointmentsVM.getGroupedFilteredDoctorsList();
return bookAppointmentsVM.isDoctorsListLoading return bookAppointmentsVM.isDoctorsListLoading
? DoctorCard( ? DoctorCard(
doctorsListResponseModel: DoctorsListResponseModel(), doctorsListResponseModel: DoctorsListResponseModel(),
@ -188,35 +199,112 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
verticalOffset: 100.0, verticalOffset: 100.0,
child: FadeInAnimation( child: FadeInAnimation(
child: AnimatedContainer( child: AnimatedContainer(
key: _itemKeys.putIfAbsent(index, () => GlobalKey()),
duration: Duration(milliseconds: 300), duration: Duration(milliseconds: 300),
curve: Curves.easeInOut, curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: DoctorCard( child: InkWell(
doctorsListResponseModel: bookAppointmentsVM.filteredDoctorList[index], onTap: () {
isLoading: false, bookAppointmentsVM.toggleGroupExpansion(index);
bookAppointmentsViewModel: bookAppointmentsViewModel, // After rebuild, ensure the expanded item is visible
).onPress(() async { WidgetsBinding.instance.addPostFrameCallback((_) {
bookAppointmentsVM.setSelectedDoctor(bookAppointmentsVM.filteredDoctorList[index]); final key = _itemKeys[index];
// bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel()); if (key != null && key.currentContext != null && bookAppointmentsVM.expandedGroupIndex == index) {
LoaderBottomSheet.showLoader(); Scrollable.ensureVisible(
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) { key.currentContext!,
LoaderBottomSheet.hideLoader(); duration: Duration(milliseconds: 350),
Navigator.of(context).push( curve: Curves.easeInOut,
CustomPageRoute( alignment: 0.1,
page: DoctorProfilePage(), );
), }
); });
}, onError: (err) { },
LoaderBottomSheet.hideLoader(); child: Padding(
showCommonBottomSheetWithoutHeight( padding: EdgeInsets.all(16.h),
context, child: Column(
child: Utils.getErrorWidget(loadingText: err), crossAxisAlignment: CrossAxisAlignment.start,
callBackFunc: () {}, children: [
isFullScreen: false, Row(
isCloseButtonVisible: true, 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<int>(index),
padding: EdgeInsets.only(top: 12.h),
child: Column(
children: groupedDoctors[index].asMap().entries.map<Widget>((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<SearchDoctorByName> {
@override @override
void dispose() { void dispose() {
_scrollController.dispose();
bookAppointmentsViewModel.doctorsList.clear(); bookAppointmentsViewModel.doctorsList.clear();
super.dispose(); super.dispose();
} }

@ -33,7 +33,6 @@ class SelectDoctorPage extends StatefulWidget {
class _SelectDoctorPageState extends State<SelectDoctorPage> { class _SelectDoctorPageState extends State<SelectDoctorPage> {
TextEditingController searchEditingController = TextEditingController(); TextEditingController searchEditingController = TextEditingController();
int? expandedIndex;
FocusNode textFocusNode = FocusNode(); FocusNode textFocusNode = FocusNode();
@ -170,8 +169,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
), ),
], ],
).paddingSymmetrical(0.h, 0.h), ).paddingSymmetrical(0.h, 0.h),
if (bookAppointmentsViewModel.isGetDocForHealthCal && bookAppointmentsVM.showSortFilterButtons) if (bookAppointmentsViewModel.isGetDocForHealthCal && bookAppointmentsVM.showSortFilterButtons) SizedBox(height: 16.h),
SizedBox(height: 16.h),
Row( Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: [ children: [
@ -190,7 +188,6 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
value: bookAppointmentsVM.isNearestAppointmentSelected, value: bookAppointmentsVM.isNearestAppointmentSelected,
onChanged: (newValue) async { onChanged: (newValue) async {
bookAppointmentsVM.setIsNearestAppointmentSelected(newValue); bookAppointmentsVM.setIsNearestAppointmentSelected(newValue);
}, },
), ),
], ],
@ -199,11 +196,10 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
padding: EdgeInsets.only(top: 16.h), padding: EdgeInsets.only(top: 16.h),
shrinkWrap: true, shrinkWrap: true,
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
itemCount: bookAppointmentsVM.isDoctorsListLoading itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : (bookAppointmentsVM.doctorsListGrouped.isNotEmpty ? bookAppointmentsVM.doctorsListGrouped.length : 1),
? 5
: (bookAppointmentsVM.doctorsListGrouped.isNotEmpty ? bookAppointmentsVM.doctorsListGrouped.length : 1),
itemBuilder: (context, index) { itemBuilder: (context, index) {
final isExpanded = expandedIndex == index; // final isExpanded = bookAppointmentsVM.expandedGroupIndex == index;
final isExpanded = true;
return bookAppointmentsVM.isDoctorsListLoading return bookAppointmentsVM.isDoctorsListLoading
? DoctorCard( ? DoctorCard(
doctorsListResponseModel: DoctorsListResponseModel(), doctorsListResponseModel: DoctorsListResponseModel(),
@ -225,13 +221,11 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: InkWell( child: InkWell(
onTap: () { onTap: () {
setState(() { bookAppointmentsVM.toggleGroupExpansion(index);
expandedIndex = isExpanded ? null : index;
});
// After rebuild, ensure the expanded item is visible // After rebuild, ensure the expanded item is visible
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
final key = _itemKeys[index]; final key = _itemKeys[index];
if (key != null && key.currentContext != null && expandedIndex == index) { if (key != null && key.currentContext != null && bookAppointmentsVM.expandedGroupIndex == index) {
Scrollable.ensureVisible( Scrollable.ensureVisible(
key.currentContext!, key.currentContext!,
duration: Duration(milliseconds: 350), duration: Duration(milliseconds: 350),
@ -282,34 +276,41 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
key: ValueKey<int>(index), key: ValueKey<int>(index),
padding: EdgeInsets.only(top: 12.h), padding: EdgeInsets.only(top: 12.h),
child: Column( child: Column(
children: bookAppointmentsVM.doctorsListGrouped[index].map<Widget>((doctor) { children: bookAppointmentsVM.doctorsListGrouped[index].asMap().entries.map<Widget>((entry) {
return Container( final doctorIndex = entry.key;
margin: EdgeInsets.only(bottom: 12.h), final doctor = entry.value;
child: DoctorCard( final isLastDoctor = doctorIndex == bookAppointmentsVM.doctorsListGrouped[index].length - 1;
doctorsListResponseModel: doctor,
isLoading: false, return Column(
bookAppointmentsViewModel: bookAppointmentsViewModel, children: [
).onPress(() async { DoctorCard(
bookAppointmentsVM.setSelectedDoctor(doctor); doctorsListResponseModel: doctor,
LoaderBottomSheet.showLoader(); isLoading: false,
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) { bookAppointmentsViewModel: bookAppointmentsViewModel,
LoaderBottomSheet.hideLoader(); ).onPress(() async {
Navigator.of(context).push( bookAppointmentsVM.setSelectedDoctor(doctor);
CustomPageRoute( LoaderBottomSheet.showLoader();
page: DoctorProfilePage(), await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
), LoaderBottomSheet.hideLoader();
); Navigator.of(context).push(
}, onError: (err) { CustomPageRoute(
LoaderBottomSheet.hideLoader(); page: DoctorProfilePage(),
showCommonBottomSheetWithoutHeight( ),
context, );
child: Utils.getErrorWidget(loadingText: err), }, onError: (err) {
callBackFunc: () {}, LoaderBottomSheet.hideLoader();
isFullScreen: false, showCommonBottomSheetWithoutHeight(
isCloseButtonVisible: true, 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(), }).toList(),
), ),

@ -38,20 +38,49 @@ class DoctorCard extends StatelessWidget {
hasShadow: false, hasShadow: false,
), ),
child: Padding( child: Padding(
padding: EdgeInsets.all(14.h), padding: EdgeInsets.only(top: 14.h,bottom: 20.h),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Column(
isLoading crossAxisAlignment: CrossAxisAlignment.center,
? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png" children: [
: doctorsListResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png", Image.network(
width: 63.h, isLoading
height: 63.h, ? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png"
fit: BoxFit.cover, : doctorsListResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png",
).circle(100).toShimmer2(isShow: isLoading), 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), SizedBox(width: 8.h),
Expanded( Expanded(
flex: 9, flex: 9,
@ -70,15 +99,19 @@ class DoctorCard extends StatelessWidget {
), ),
SizedBox(height: 2.h), SizedBox(height: 2.h),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
(isLoading SizedBox(
? "Consultant Cardiologist" width: MediaQuery.of(context).size.width * 0.45,
: doctorsListResponseModel.speciality!.isNotEmpty child: (isLoading
? doctorsListResponseModel.speciality!.first ? "Consultant Cardiologist"
: "") : doctorsListResponseModel.speciality!.isNotEmpty
.toString() ? doctorsListResponseModel.speciality!.first
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 1) : "")
.toShimmer2(isShow: isLoading), .toString()
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 2)
.toShimmer2(isShow: isLoading),
),
SizedBox(width: 6.w), SizedBox(width: 6.w),
Image.network( Image.network(
isLoading ? "https://hmgwebservices.com/Images/flag/SYR.png" : doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png", 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( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
spacing: 3.h, spacing: 3.h,
runSpacing: 4.h, runSpacing: 4.h,
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
labelText: "Clinic: ${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}".needTranslation, labelText: "${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}".needTranslation,
).toShimmer2(isShow: isLoading), ).toShimmer2(isShow: isLoading),
AppCustomChipWidget( AppCustomChipWidget(
labelText: "Branch: ${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}".needTranslation, labelText: "${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}".needTranslation,
).toShimmer2(isShow: isLoading), ).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 bookAppointmentsViewModel.isNearestAppointmentSelected
? doctorsListResponseModel.nearestFreeSlot != null ? doctorsListResponseModel.nearestFreeSlot != null
? AppCustomChipWidget( ? AppCustomChipWidget(

@ -4,15 +4,18 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:get_it/get_it.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_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.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/enums.dart';
import 'package:hmg_patient_app_new/core/utils/utils.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/route_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_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/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/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/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/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/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/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/features/water_monitor/water_monitor_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.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 '../../core/dependencies.dart' show getIt;
import '../../features/qr_parking/qr_parking_view_model.dart'; import '../../features/qr_parking/qr_parking_view_model.dart';
import '../emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart';
class ServicesPage extends StatelessWidget { class ServicesPage extends StatelessWidget {
ServicesPage({super.key}); ServicesPage({super.key});
@ -46,27 +50,24 @@ class ServicesPage extends StatelessWidget {
late MedicalFileViewModel medicalFileViewModel; late MedicalFileViewModel medicalFileViewModel;
late final List<HmgServicesComponentModel> hmgServices = [ late final List<HmgServicesComponentModel> hmgServices = [
HmgServicesComponentModel( HmgServicesComponentModel(11, "Emergency Services".needTranslation, "".needTranslation, AppAssets.emergency_services_icon, bgColor: AppColors.primaryRedColor, true, route: null, onTap: () async {
11, if (getIt.get<AppState>().isAuthenticated) {
"Emergency Services".needTranslation, getIt.get<EmergencyServicesViewModel>().flushData();
"".needTranslation, getIt.get<EmergencyServicesViewModel>().getTransportationOrders(
AppAssets.emergency_services_icon, showLoader: false,
bgColor: AppColors.primaryRedColor, );
true, getIt.get<EmergencyServicesViewModel>().getRRTOrders(
route: null, onTap: () { showLoader: false,
getIt.get<EmergencyServicesViewModel>().flushData(); );
getIt.get<EmergencyServicesViewModel>().getTransportationOrders( Navigator.of(GetIt.instance<NavigationService>().navigatorKey.currentContext!).push(
showLoader: false, CustomPageRoute(
); page: EmergencyServicesPage(),
getIt.get<EmergencyServicesViewModel>().getRRTOrders( settings: const RouteSettings(name: '/EmergencyServicesPage'),
showLoader: false, ),
); );
Navigator.of(GetIt.instance<NavigationService>().navigatorKey.currentContext!).push( } else {
CustomPageRoute( await getIt.get<AuthenticationViewModel>().onLoginPressed();
page: EmergencyServicesPage(), }
settings: const RouteSettings(name: '/EmergencyServicesPage'),
),
);
}), }),
HmgServicesComponentModel( HmgServicesComponentModel(
11, 11,
@ -77,15 +78,13 @@ class ServicesPage extends StatelessWidget {
true, true,
route: AppRoutes.bookAppointmentPage, route: AppRoutes.bookAppointmentPage,
), ),
HmgServicesComponentModel( HmgServicesComponentModel(5, "Complete Checkup".needTranslation, "".needTranslation, AppAssets.comprehensiveCheckup, bgColor: AppColors.bgGreenColor, true, route: null, onTap: () async {
5, if (getIt.get<AppState>().isAuthenticated) {
"Complete Checkup".needTranslation, getIt.get<NavigationService>().pushPageRoute(AppRoutes.comprehensiveCheckupPage);
"".needTranslation, } else {
AppAssets.comprehensiveCheckup, await getIt.get<AuthenticationViewModel>().onLoginPressed();
bgColor: AppColors.bgGreenColor, }
true, }),
route: AppRoutes.comprehensiveCheckupPage,
),
HmgServicesComponentModel( HmgServicesComponentModel(
11, 11,
"Indoor Navigation".needTranslation, "Indoor Navigation".needTranslation,
@ -94,17 +93,51 @@ class ServicesPage extends StatelessWidget {
bgColor: Color(0xff45A2F8), bgColor: Color(0xff45A2F8),
true, true,
route: null, route: null,
onTap: () {}, onTap: () {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.selectHospital.tr(),
GetIt.instance<NavigationService>().navigatorKey.currentContext!,
child: ChangeNotifierProvider(
create: (context) => HospitalSelectionBottomSheetViewModel(getIt()),
child: Consumer<HospitalSelectionBottomSheetViewModel>(
builder: (_, vm, __) => HospitalBottomSheetBody(
searchText: vm.searchController,
displayList: vm.displayList,
onFacilityClicked: (value) {
vm.setSelectedFacility(value);
vm.getDisplayList();
},
onHospitalClicked: (hospital) {
Navigator.pop(GetIt.instance<NavigationService>().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<NavigationService>().navigatorKey.currentContext!.read<EmergencyServicesViewModel>().clearSearchText();
},
);
},
), ),
HmgServicesComponentModel( HmgServicesComponentModel(
11, 11, "E-Referral Services".needTranslation, "".needTranslation, AppAssets.eReferral, bgColor: AppColors.eReferralCardColor, true, route: null, onTap: () async {
"E-Referral Services".needTranslation, if (getIt.get<AppState>().isAuthenticated) {
"".needTranslation, getIt.get<NavigationService>().pushPageRoute(AppRoutes.eReferralPage);
AppAssets.eReferral, } else {
bgColor: AppColors.eReferralCardColor, await getIt.get<AuthenticationViewModel>().onLoginPressed();
true, }
route: AppRoutes.eReferralPage, }),
),
HmgServicesComponentModel( HmgServicesComponentModel(
3, 3,
"Blood Donation".needTranslation, "Blood Donation".needTranslation,
@ -182,7 +215,14 @@ class ServicesPage extends StatelessWidget {
AppAssets.general_health, AppAssets.general_health,
bgColor: AppColors.whiteColor, bgColor: AppColors.whiteColor,
true, true,
route: AppRoutes.healthTrackersPage, route: null,
onTap: () async {
if (getIt.get<AppState>().isAuthenticated) {
getIt.get<NavigationService>().pushPageRoute(AppRoutes.healthTrackersPage);
} else {
await getIt.get<AuthenticationViewModel>().onLoginPressed();
}
},
), ),
HmgServicesComponentModel( HmgServicesComponentModel(
11, 11,
@ -193,24 +233,28 @@ class ServicesPage extends StatelessWidget {
true, true,
route: null, // Set to null since we handle navigation in onTap route: null, // Set to null since we handle navigation in onTap
onTap: () async { onTap: () async {
LoaderBottomSheet.showLoader(loadingText: "Fetching your water intake details.".needTranslation); if (getIt.get<AppState>().isAuthenticated) {
final waterMonitorVM = getIt.get<WaterMonitorViewModel>(); LoaderBottomSheet.showLoader(loadingText: "Fetching your water intake details.".needTranslation);
final context = getIt.get<NavigationService>().navigatorKey.currentContext!; final waterMonitorVM = getIt.get<WaterMonitorViewModel>();
await waterMonitorVM.fetchUserDetailsForMonitoring( final context = getIt.get<NavigationService>().navigatorKey.currentContext!;
onSuccess: (userDetail) { await waterMonitorVM.fetchUserDetailsForMonitoring(
LoaderBottomSheet.hideLoader(); onSuccess: (userDetail) {
if (userDetail == null) { LoaderBottomSheet.hideLoader();
waterMonitorVM.populateFromAuthenticatedUser(); if (userDetail == null) {
context.navigateWithName(AppRoutes.waterMonitorSettingsPage); waterMonitorVM.populateFromAuthenticatedUser();
} else { context.navigateWithName(AppRoutes.waterMonitorSettingsPage);
} else {
context.navigateWithName(AppRoutes.waterConsumptionPage);
}
},
onError: (error) {
LoaderBottomSheet.hideLoader();
context.navigateWithName(AppRoutes.waterConsumptionPage); context.navigateWithName(AppRoutes.waterConsumptionPage);
} },
}, );
onError: (error) { } else {
LoaderBottomSheet.hideLoader(); await getIt.get<AuthenticationViewModel>().onLoginPressed();
context.navigateWithName(AppRoutes.waterConsumptionPage); }
},
);
}, },
), ),
HmgServicesComponentModel( HmgServicesComponentModel(
@ -238,7 +282,14 @@ class ServicesPage extends StatelessWidget {
AppAssets.smartwatch_icon, AppAssets.smartwatch_icon,
bgColor: AppColors.whiteColor, bgColor: AppColors.whiteColor,
true, true,
route: AppRoutes.smartWatches, route: null,
onTap: () async {
if (getIt.get<AppState>().isAuthenticated) {
getIt.get<NavigationService>().pushPageRoute(AppRoutes.smartWatches);
} else {
await getIt.get<AuthenticationViewModel>().onLoginPressed();
}
},
// route: AppRoutes.huaweiHealthExample, // route: AppRoutes.huaweiHealthExample,
), ),
]; ];
@ -331,14 +382,17 @@ class ServicesPage extends StatelessWidget {
], ],
), ),
Spacer(), Spacer(),
Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) { getIt.get<AppState>().isAuthenticated
return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, isExpanded: false) ? Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
.toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h); 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(), Spacer(),
CustomButton( getIt.get<AppState>().isAuthenticated
height: 40.h, ? CustomButton(
icon: AppAssets.recharge_icon, height: 40.h,
icon: AppAssets.recharge_icon,
iconSize: 16.w, iconSize: 16.w,
iconColor: AppColors.infoColor, iconColor: AppColors.infoColor,
textColor: AppColors.infoColor, textColor: AppColors.infoColor,
@ -352,10 +406,15 @@ class ServicesPage extends StatelessWidget {
onPressed: () { onPressed: () {
Navigator.of(context).push(CustomPageRoute(page: RechargeWalletPage())); Navigator.of(context).push(CustomPageRoute(page: RechargeWalletPage()));
}, },
), )
: SizedBox.shrink(),
], ],
).onPress(() { ).onPress(() async {
Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage())); if (getIt.get<AppState>().isAuthenticated) {
Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage()));
} else {
await getIt.get<AuthenticationViewModel>().onLoginPressed();
}
}), }),
), ),
), ),
@ -377,13 +436,14 @@ class ServicesPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.services_medical_file_icon, width: 30.w, height: 30.h), 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), Utils.buildSvgWithAssets(icon: AppAssets.arrow_forward),
], ],
), ),
Spacer(), Spacer(),
Wrap( getIt.get<AppState>().isAuthenticated
spacing: -8.h, ? Wrap(
spacing: -8.h,
// runSpacing: 0.h, // runSpacing: 0.h,
children: [ children: [
Utils.buildImgWithAssets( Utils.buildImgWithAssets(
@ -411,10 +471,12 @@ class ServicesPage extends StatelessWidget {
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
], ],
), )
: "Login to view your medical file".needTranslation.toText12(fontWeight: FontWeight.w500, maxLine: 2),
Spacer(), Spacer(),
CustomButton( getIt.get<AppState>().isAuthenticated
height: 40.h, ? CustomButton(
height: 40.h,
icon: AppAssets.add_icon, icon: AppAssets.add_icon,
iconSize: 16.w, iconSize: 16.w,
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
@ -436,14 +498,19 @@ class ServicesPage extends StatelessWidget {
medicalFileViewModel.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms); medicalFileViewModel.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms);
}); });
}, },
), )
: SizedBox.shrink(),
], ],
).onPress(() { ).onPress(() async {
Navigator.of(context).push( if (getIt.get<AppState>().isAuthenticated) {
CustomPageRoute( Navigator.of(context).push(
page: MedicalFilePage(), CustomPageRoute(
), page: MedicalFilePage(),
); ),
);
} else {
await getIt.get<AuthenticationViewModel>().onLoginPressed();
}
}), }),
), ),
), ),
@ -539,7 +606,6 @@ class ServicesPage extends StatelessWidget {
), ),
), ),
); );
}), }),
), ),
), ),

@ -46,7 +46,7 @@ class LandingPageData {
), ),
ServiceCardData( ServiceCardData(
serviceName: "health_converters", serviceName: "health_converters",
icon: AppAssets.health_calculators_icon, icon: AppAssets.health_converters_icon,
title: "Health", title: "Health",
subtitle: "Converters", subtitle: "Converters",
backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,
@ -56,7 +56,7 @@ class LandingPageData {
), ),
ServiceCardData( ServiceCardData(
serviceName: "parking_guide", serviceName: "parking_guide",
icon: AppAssets.health_calculators_icon, icon: AppAssets.car_parking_icon,
title: "Parking", title: "Parking",
subtitle: "Guide", subtitle: "Guide",
backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,

@ -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/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/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/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/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/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/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/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/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/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/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.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/small_service_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/welcome_widget.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/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/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/rate_appointment/rate_appointment_doctor.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/ancillary_procedures_details_page.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 '../active_medication/active_medication_page.dart';
import '../emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart';
class LandingPage extends StatefulWidget { class LandingPage extends StatefulWidget {
const LandingPage({super.key}); const LandingPage({super.key});
@ -82,6 +87,7 @@ class _LandingPageState extends State<LandingPage> {
late BookAppointmentsViewModel bookAppointmentsViewModel; late BookAppointmentsViewModel bookAppointmentsViewModel;
late EmergencyServicesViewModel emergencyServicesViewModel; late EmergencyServicesViewModel emergencyServicesViewModel;
late TodoSectionViewModel todoSectionViewModel; late TodoSectionViewModel todoSectionViewModel;
late NotificationsViewModel notificationsViewModel;
final SwiperController _controller = SwiperController(); final SwiperController _controller = SwiperController();
@ -108,22 +114,26 @@ class _LandingPageState extends State<LandingPage> {
myAppointmentsViewModel.getPatientAppointments(true, false); myAppointmentsViewModel.getPatientAppointments(true, false);
emergencyServicesViewModel.checkPatientERAdvanceBalance(); emergencyServicesViewModel.checkPatientERAdvanceBalance();
myAppointmentsViewModel.getPatientAppointmentQueueDetails(); myAppointmentsViewModel.getPatientAppointmentQueueDetails();
if(!appState.isRatedVisible) { notificationsViewModel.initNotificationsViewModel();
appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) {
if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) { // Commented as per new requirement to remove rating popup from the app
appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!,
onSuccess: ((response) { // if(!appState.isRatedVisible) {
appointmentRatingViewModel.setClinicOrDoctor(false); // appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) {
appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation); // if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) {
appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation); // appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!,
openLastRating(); // onSuccess: ((response) {
appState.setRatedVisible(true); // appointmentRatingViewModel.setClinicOrDoctor(false);
}), // appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation);
); // appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation);
} // openLastRating();
}, // appState.setRatedVisible(true);
); // }),
} // );
// }
// },
// );
// }
} }
}); });
super.initState(); super.initState();
@ -137,6 +147,7 @@ class _LandingPageState extends State<LandingPage> {
immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false);
emergencyServicesViewModel = Provider.of<EmergencyServicesViewModel>(context, listen: false); emergencyServicesViewModel = Provider.of<EmergencyServicesViewModel>(context, listen: false);
todoSectionViewModel = Provider.of<TodoSectionViewModel>(context, listen: false); todoSectionViewModel = Provider.of<TodoSectionViewModel>(context, listen: false);
notificationsViewModel = Provider.of<NotificationsViewModel>(context, listen: false);
appState = getIt.get<AppState>(); appState = getIt.get<AppState>();
return PopScope( return PopScope(
canPop: false, canPop: false,
@ -182,37 +193,65 @@ class _LandingPageState extends State<LandingPage> {
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
height: 40.h, height: 40.h,
), ),
Row( Consumer<TodoSectionViewModel>(builder: (context, todoSectionVM, child) {
mainAxisSize: MainAxisSize.min, return Row(
spacing: 12.h, mainAxisSize: MainAxisSize.min,
children: [ spacing: 12.h,
Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 18.h, width: 18.h).onPress(() { children: [
Navigator.of(context).push( Stack(children: [
CustomPageRoute( Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 18.h, width: 18.h).onPress(() async {
page: MedicalFilePage(), if (appState.isAuthenticated) {
// page: LoginScreen(), notificationsViewModel.setNotificationStatusID(2);
), notificationsViewModel.getAllNotifications();
); Navigator.of(context).push(
}), CustomPageRoute(
Utils.buildSvgWithAssets(icon: AppAssets.indoor_nav_icon, height: 18.h, width: 18.h).onPress(() { page: NotificationsListPage(),
// Navigator.of(context).push( // page: LoginScreen(),
// CustomPageRoute( ),
// page: MedicalFilePage(), );
// // page: LoginScreen(), } else {
// ), await authVM.onLoginPressed();
// ); }
}), }),
Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 18.h, width: 18.h).onPress(() { (appState.isAuthenticated && (int.parse(todoSectionVM.notificationsCount ?? "0") > 0))
showCommonBottomSheetWithoutHeight( ? Positioned(
context, right: 0,
title: LocaleKeys.contactUs.tr(), top: 0,
child: ContactUs(), child: Container(
callBackFunc: () {}, width: 8.w,
isFullScreen: false, 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), ).paddingSymmetrical(24.h, 0.h),
!appState.isAuthenticated !appState.isAuthenticated
@ -312,15 +351,49 @@ class _LandingPageState extends State<LandingPage> {
isFromHomePage: true, isFromHomePage: true,
), ),
).paddingSymmetrical(24.h, 0.h) ).paddingSymmetrical(24.h, 0.h)
: Swiper( : isTablet
itemCount: myAppointmentsVM.isMyAppointmentsLoading ? SizedBox(
? 3 height: isFoldable ? 290.h : 255.h,
: myAppointmentsVM.patientAppointmentsHistoryList.length < 3 child: ListView.separated(
? myAppointmentsVM.patientAppointmentsHistoryList.length scrollDirection: Axis.horizontal,
: 3, itemCount: 3,
layout: SwiperLayout.STACK, shrinkWrap: true,
loop: true, padding: EdgeInsets.only(left: 16.h, right: 16.h),
itemWidth: MediaQuery.of(context).size.width - 48.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, indicatorLayout: PageIndicatorLayout.COLOR,
axisDirection: AxisDirection.right, axisDirection: AxisDirection.right,
controller: _controller, controller: _controller,
@ -331,208 +404,7 @@ class _LandingPageState extends State<LandingPage> {
builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor),
), ),
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return (myAppointmentsVM.isPatientHasQueueAppointment && index == 0) return getIndexSwiperCard(index);
? 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<Duration>(
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,
),
);
}, },
) )
: Container( : Container(
@ -773,6 +645,288 @@ class _LandingPageState extends State<LandingPage> {
); );
} }
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<Duration>(
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) { void showQuickLogin(BuildContext context) {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
@ -831,4 +985,41 @@ class _LandingPageState extends State<LandingPage> {
isFullScreen: false, isFullScreen: false,
); );
} }
void openIndoorNavigationBottomSheet(BuildContext context) {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.selectHospital.tr(),
context,
child: ChangeNotifierProvider(
create: (context) => HospitalSelectionBottomSheetViewModel(getIt()),
child: Consumer<HospitalSelectionBottomSheetViewModel>(
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<EmergencyServicesViewModel>().clearSearchText();
},
);
}
} }

@ -1,17 +1,22 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_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/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/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/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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.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/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/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_orders_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/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/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
@ -126,6 +131,39 @@ class SmallServiceCard extends StatelessWidget {
case "indoor_navigation": case "indoor_navigation":
openIndoorNavigationBottomSheet(context); 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<QrParkingViewModel>(),
child: const ParkingPage(),
),
),
);
break;
default: default:
// Handle unknown service // Handle unknown service
break; break;

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

@ -55,7 +55,7 @@ class _ParkingPageState extends State<ParkingPage> {
children: [ children: [
Expanded( Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [

@ -217,12 +217,12 @@ class ProfileSettingsState extends State<ProfileSettings> {
child: Column( child: Column(
children: [ children: [
actionItem(AppAssets.email_transparent, "Update Email Address".needTranslation, () {}), actionItem(AppAssets.email_transparent, "Update Email Address".needTranslation, () {}),
1.divider, // 1.divider,
actionItem(AppAssets.smart_phone_fill, "Phone Number".needTranslation, () {}), // actionItem(AppAssets.smart_phone_fill, "Phone Number".needTranslation, () {}),
1.divider, // 1.divider,
actionItem(AppAssets.my_address, "My Addresses".needTranslation, () {}), // actionItem(AppAssets.my_address, "My Addresses".needTranslation, () {}),
1.divider, // 1.divider,
actionItem(AppAssets.emergency, "Emergency Contact".needTranslation, () {}), // actionItem(AppAssets.emergency, "Emergency Contact".needTranslation, () {}),
], ],
), ),
), ),

@ -266,7 +266,11 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
fit: BoxFit.contain, fit: BoxFit.contain,
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() {
if (!todoSectionViewModel.isProcessingPayment) { if (!todoSectionViewModel.isProcessingPayment) {
_startApplePay(); if (Utils.havePrivilege(103)) {
_startApplePay();
} else {
_openPaymentURL("ApplePay");
}
} }
}) })
: SizedBox(height: 12.h), : SizedBox(height: 12.h),

@ -133,21 +133,17 @@ class AppRoutes {
return HealthTrackerDetailPage( return HealthTrackerDetailPage(
trackerType: args ?? HealthTrackerTypeEnum.bloodSugar, trackerType: args ?? HealthTrackerTypeEnum.bloodSugar,
); );
},
monthlyReports: (context) =>
ChangeNotifierProvider(
create: (_) =>
MonthlyReportsViewModel(
monthlyReportsRepo: getIt<MonthlyReportsRepo>(),
errorHandlerService: getIt<ErrorHandlerService>(),
),
child: const MonthlyReportsPage(),
),
qrParking: (context) => ChangeNotifierProvider<QrParkingViewModel>(
create: (_) => getIt<QrParkingViewModel>(),
child: const ParkingPage(),
)
};
// monthlyReports: (context) => ChangeNotifierProvider(
// create: (_) => MonthlyReportsViewModel(
// monthlyReportsRepo: getIt<MonthlyReportsRepo>(),
// errorHandlerService: getIt<ErrorHandlerService>(),
// ),
// child: const MonthlyReportsPage(),
// ),
// qrParking: (context) => ChangeNotifierProvider<QrParkingViewModel>(
// create: (_) => getIt<QrParkingViewModel>(),
// child: const ParkingPage(),
},
};
} }

@ -42,15 +42,15 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
? RotatedBox( ? RotatedBox(
quarterTurns: 90, quarterTurns: 90,
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: AppAssets.arrow_back, icon: AppAssets.forward_top_nav_icon,
width: 32.w, width: 24.w,
height: 32.h, height: 24.h,
), ),
) )
: Utils.buildSvgWithAssets( : Utils.buildSvgWithAssets(
icon: AppAssets.arrow_back, icon: AppAssets.forward_top_nav_icon,
width: 32.w, width: 24.w,
height: 32.h, height: 24.h,
), ),
), ),
), ),

@ -89,7 +89,7 @@ class CollapsingListView extends StatelessWidget {
? Transform.flip( ? Transform.flip(
flipX: appState.isArabic(), flipX: appState.isArabic(),
child: IconButton( 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), padding: EdgeInsets.only(left: 12),
onPressed: () { onPressed: () {
if (leadingCallback != null) { if (leadingCallback != null) {

Loading…
Cancel
Save