Merge branch 'refs/heads/master' into fatima

# Conflicts:
#	lib/core/dependencies.dart
#	lib/routes/app_routes.dart
pull/157/head
Fatimah.Alshammari 1 day ago
commit 97a47ce4e8

@ -160,6 +160,7 @@ dependencies {
annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
implementation("com.mapbox.maps:android:11.5.0")
implementation("com.mapbox.mapboxsdk:mapbox-sdk-turf:7.3.1")
// implementation("com.mapbox.maps:android:11.4.0")
// AARs

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

@ -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) {
PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey, showProgress: true) { [weak self] token, error in
PenNavUIManager.shared.getToken(clientID: config.clientID, clientKey: config.clientKey, showProgress: false) { [weak self] token, error in
if let error = error {
let errorMessage = "Token error while getting the for Navigate to method"

@ -312,6 +312,8 @@ class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitia
print("====== after eventsDelegate onPenNavSuccess =========")
PenNavUIManager.shared.navigate(to: "108")
@ -385,21 +387,21 @@ class PenguinView: NSObject, FlutterPlatformView, PIEventsDelegate, PenNavInitia
self?.handleNavigation(clinicID: clinicID, token: token) { success, errorMessage in
if success {
print("Navigation successful")
} else {
print("Navigation failed: \(errorMessage ?? "Unknown error")")
}
}
// self?.handleNavigation(clinicID: clinicID, token: token) { success, errorMessage in
//
// if success {
//
// print("Navigation successful")
//
// } else {
//
// print("Navigation failed: \(errorMessage ?? "Unknown error")")
//
// }
//
//
//
// }

@ -398,6 +398,22 @@ var GET_COVID_DRIVETHRU_PROCEDURES_LIST = 'Services/Doctors.svc/REST/COVID19_Get
var GET_PATIENT_LAST_RECORD = 'Services/Patients.svc/REST/Med_GetPatientLastRecord';
var INSERT_PATIENT_HEALTH_DATA = 'Services/Patients.svc/REST/Med_InsertTransactions';
///Push Notifications
var GET_ALL_NOTIFICATIONS = 'Services/MobileNotifications.svc/REST/PushNotification_GetAllNotifications';
///My Trackers
var GET_DIABETIC_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetDiabeticResultAverage';
var GET_DIABTEC_RESULT = 'Services/Patients.svc/REST/Patient_GetDiabtecResults';
var ADD_DIABTEC_RESULT = 'Services/Patients.svc/REST/Patient_AddDiabtecResult';
var GET_BLOOD_PRESSURE_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetBloodPressureResultAverage';
var GET_BLOOD_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_GetBloodPressureResult';
var ADD_BLOOD_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_AddBloodPressureResult';
var GET_WEIGHT_PRESSURE_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResultAverage';
var GET_WEIGHT_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResult';
var ADD_WEIGHT_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_AddWeightMeasurementResult';
var ADD_ACTIVE_PRESCRIPTIONS_REPORT_BY_PATIENT_ID = 'Services/Patients.svc/Rest/GetActivePrescriptionReportByPatientID';
var GET_CALL_INFO_HOURS_RESULT = 'Services/Doctors.svc/REST/GetCallInfoHoursResult';
@ -658,10 +674,12 @@ const FAMILY_FILES = 'Services/Authentication.svc/REST/GetAllSharedRecordsByStat
var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatbot_SendMedicationInstructionByWhatsApp';
const DASHBOARD = 'Services/Patients.svc/REST/PatientDashboard';
class ApiConsts {
static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT

@ -225,6 +225,9 @@ class AppAssets {
static const String trade_down_red = '$svgBasePath/trade_down_red.svg';
static const String pharmacy_icon = '$svgBasePath/phramacy_icon.svg';
static const String forward_top_nav_icon = '$svgBasePath/forward_top_nav_icon.svg';
static const String back_top_nav_icon = '$svgBasePath/back_top_nav_icon.svg';
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';
static const String bookAppoBottom = '$svgBasePath/book_appo_bottom.svg';

@ -35,12 +35,15 @@ import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/monthly_report/monthly_report_repo.dart';
import 'package:hmg_patient_app_new/features/monthly_report/monthly_report_view_model.dart';
import 'package:hmg_patient_app_new/features/monthly_reports/monthly_reports_repo.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_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.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_view_model.dart';
import 'package:hmg_patient_app_new/features/notifications/notifications_repo.dart';
import 'package:hmg_patient_app_new/features/notifications/notifications_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart';
@ -170,6 +173,7 @@ class AppDependencies {
cacheService: getIt<CacheService>(),
),
);
getIt.registerLazySingleton<NotificationsRepo>(() => NotificationsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
// ViewModels
// Global/shared VMs LazySingleton
@ -289,14 +293,12 @@ class AppDependencies {
getIt.registerLazySingleton<WaterMonitorViewModel>(() => WaterMonitorViewModel(waterMonitorRepo: getIt(), errorHandlerService: getIt()));
//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<MyInvoicesViewModel>(() => MyInvoicesViewModel(
myInvoicesRepo: getIt(),
errorHandlerService: getIt(),
navServices: getIt(),
));
getIt.registerLazySingleton<NotificationsViewModel>(() => NotificationsViewModel(notificationsRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<HealthTrackersViewModel>(() => HealthTrackersViewModel(healthTrackersRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<ActivePrescriptionsViewModel>(
() => ActivePrescriptionsViewModel(
@ -304,13 +306,13 @@ class AppDependencies {
activePrescriptionsRepo: getIt()
),
);
// getIt.registerFactory<QrParkingViewModel>(
// () => QrParkingViewModel(
// qrParkingRepo: getIt<QrParkingRepo>(),
// errorHandlerService: getIt<ErrorHandlerService>(),
// cacheService: getIt<CacheService>(),
// ),
// );
getIt.registerFactory<QrParkingViewModel>(
() => QrParkingViewModel(
qrParkingRepo: getIt<QrParkingRepo>(),
errorHandlerService: getIt<ErrorHandlerService>(),
cacheService: getIt<CacheService>(),
),
);
}
}

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

@ -289,9 +289,24 @@ class BookAppointmentsViewModel extends ChangeNotifier {
this.isNearestAppointmentSelected = isNearestAppointmentSelected;
if (isNearestAppointmentSelected) {
doctorsList.sort((a, b) => DateUtil.convertStringToDate(a.nearestFreeSlot!).compareTo(DateUtil.convertStringToDate(b.nearestFreeSlot!)));
for (var group in doctorsListGrouped) {
group.sort((a, b) {
var aSlot = a.nearestFreeSlot;
var bSlot = b.nearestFreeSlot;
if (aSlot == null || bSlot == null) return 0;
return DateUtil.convertStringToDate(aSlot).compareTo(DateUtil.convertStringToDate(bSlot));
});
}
} else {
doctorsList.sort((a, b) => b.decimalDoctorRate!.compareTo(a.decimalDoctorRate!));
for (var group in doctorsListGrouped) {
group.sort((a, b) {
var aSlot = a.decimalDoctorRate;
var bSlot = b.decimalDoctorRate;
if (aSlot == null || bSlot == null) return 0;
return bSlot.compareTo(aSlot);
});
}
// doctorsList.sort((a, b) => b.decimalDoctorRate!.compareTo(a.decimalDoctorRate!));
}
notifyListeners();

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

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

@ -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>> getPatientDashboard();
}
class TodoSectionRepoImp implements TodoSectionRepo {
@ -376,4 +378,29 @@ class TodoSectionRepoImp implements TodoSectionRepo {
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});
String? notificationsCount = "0";
initializeTodoSectionViewModel() async {
patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true;
isAncillaryDetailsProceduresLoading = true;
await getPatientOnlineAncillaryOrderList();
notificationsCount = "0";
getPatientOnlineAncillaryOrderList();
getPatientDashboard();
}
bool isAncillaryOrdersLoading = false;
@ -28,6 +32,28 @@ class TodoSectionViewModel extends ChangeNotifier {
notifyListeners();
}
Future<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 {
patientAncillaryOrdersList.clear();
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/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_view_model.dart';
import 'package:hmg_patient_app_new/features/notifications/notifications_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
@ -184,6 +185,9 @@ void main() async {
ChangeNotifierProvider<MonthlyReportViewModel>(
create: (_) => getIt.get<MonthlyReportViewModel>(),
),
ChangeNotifierProvider<NotificationsViewModel>(
create: (_) => getIt.get<NotificationsViewModel>(),
),
ChangeNotifierProvider<ActivePrescriptionsViewModel>(
create: (_) => getIt.get<ActivePrescriptionsViewModel>(),
),

@ -262,7 +262,6 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
final doctorIndex = entry.key;
final doctor = entry.value;
final isLastDoctor = doctorIndex == groupedDoctors[index].length - 1;
return Column(
children: [
DoctorCard(

@ -198,7 +198,8 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
physics: NeverScrollableScrollPhysics(),
itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : (bookAppointmentsVM.doctorsListGrouped.isNotEmpty ? bookAppointmentsVM.doctorsListGrouped.length : 1),
itemBuilder: (context, index) {
final isExpanded = bookAppointmentsVM.expandedGroupIndex == index;
// final isExpanded = bookAppointmentsVM.expandedGroupIndex == index;
final isExpanded = true;
return bookAppointmentsVM.isDoctorsListLoading
? DoctorCard(
doctorsListResponseModel: DoctorsListResponseModel(),

@ -43,15 +43,44 @@ class DoctorCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
isLoading
? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png"
: doctorsListResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png",
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: isLoading),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.network(
isLoading
? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png"
: doctorsListResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png",
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: isLoading),
Transform.translate(
offset: Offset(0.0, -20.h),
child: Container(
width: 40.w,
height: 40.h,
decoration: BoxDecoration(
color: AppColors.whiteColor,
shape: BoxShape.circle, // Makes the container circular
border: Border.all(
color: AppColors.scaffoldBgColor, // Color of the border
width: 1.5.w, // Width of the border
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h),
SizedBox(height: 2.h),
"${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor),
],
),
).circle(100).toShimmer2(isShow: isLoading),
),
],
),
SizedBox(width: 8.h),
Expanded(
flex: 9,
@ -70,15 +99,19 @@ class DoctorCard extends StatelessWidget {
),
SizedBox(height: 2.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
(isLoading
? "Consultant Cardiologist"
: doctorsListResponseModel.speciality!.isNotEmpty
? doctorsListResponseModel.speciality!.first
: "")
.toString()
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 1)
.toShimmer2(isShow: isLoading),
SizedBox(
width: MediaQuery.of(context).size.width * 0.45,
child: (isLoading
? "Consultant Cardiologist"
: doctorsListResponseModel.speciality!.isNotEmpty
? doctorsListResponseModel.speciality!.first
: "")
.toString()
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 2)
.toShimmer2(isShow: isLoading),
),
SizedBox(width: 6.w),
Image.network(
isLoading ? "https://hmgwebservices.com/Images/flag/SYR.png" : doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
@ -98,23 +131,17 @@ class DoctorCard extends StatelessWidget {
),
],
),
SizedBox(height: 12.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
labelText: "Clinic: ${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}".needTranslation,
labelText: "${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}".needTranslation,
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: "Branch: ${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}".needTranslation,
labelText: "${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}".needTranslation,
).toShimmer2(isShow: isLoading),
doctorsListResponseModel.decimalDoctorRate != null ? AppCustomChipWidget(
icon: AppAssets.rating_icon,
iconColor: AppColors.ratingColorYellow,
labelText: "Rating: ${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".needTranslation,
).toShimmer2(isShow: isLoading) : SizedBox(),
bookAppointmentsViewModel.isNearestAppointmentSelected
? doctorsListResponseModel.nearestFreeSlot != null
? AppCustomChipWidget(

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

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

@ -21,11 +21,13 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/hospital/hospital_selection_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_rating_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/notifications/notifications_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
@ -44,6 +46,7 @@ import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card
import 'package:hmg_patient_app_new/presentation/home/widgets/small_service_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/welcome_widget.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/notifications/notifications_list_page.dart';
import 'package:hmg_patient_app_new/presentation/profile_settings/profile_settings.dart';
import 'package:hmg_patient_app_new/presentation/rate_appointment/rate_appointment_doctor.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/ancillary_procedures_details_page.dart';
@ -61,6 +64,8 @@ import 'package:provider/provider.dart';
import '../active_medication/active_medication_page.dart';
import '../emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart';
class LandingPage extends StatefulWidget {
const LandingPage({super.key});
@ -82,6 +87,7 @@ class _LandingPageState extends State<LandingPage> {
late BookAppointmentsViewModel bookAppointmentsViewModel;
late EmergencyServicesViewModel emergencyServicesViewModel;
late TodoSectionViewModel todoSectionViewModel;
late NotificationsViewModel notificationsViewModel;
final SwiperController _controller = SwiperController();
@ -108,22 +114,26 @@ class _LandingPageState extends State<LandingPage> {
myAppointmentsViewModel.getPatientAppointments(true, false);
emergencyServicesViewModel.checkPatientERAdvanceBalance();
myAppointmentsViewModel.getPatientAppointmentQueueDetails();
if(!appState.isRatedVisible) {
appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) {
if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) {
appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!,
onSuccess: ((response) {
appointmentRatingViewModel.setClinicOrDoctor(false);
appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation);
appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation);
openLastRating();
appState.setRatedVisible(true);
}),
);
}
},
);
}
notificationsViewModel.initNotificationsViewModel();
// Commented as per new requirement to remove rating popup from the app
// if(!appState.isRatedVisible) {
// appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) {
// if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) {
// appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!,
// onSuccess: ((response) {
// appointmentRatingViewModel.setClinicOrDoctor(false);
// appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation);
// appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation);
// openLastRating();
// appState.setRatedVisible(true);
// }),
// );
// }
// },
// );
// }
}
});
super.initState();
@ -137,6 +147,7 @@ class _LandingPageState extends State<LandingPage> {
immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false);
emergencyServicesViewModel = Provider.of<EmergencyServicesViewModel>(context, listen: false);
todoSectionViewModel = Provider.of<TodoSectionViewModel>(context, listen: false);
notificationsViewModel = Provider.of<NotificationsViewModel>(context, listen: false);
appState = getIt.get<AppState>();
return PopScope(
canPop: false,
@ -182,37 +193,65 @@ class _LandingPageState extends State<LandingPage> {
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
height: 40.h,
),
Row(
mainAxisSize: MainAxisSize.min,
spacing: 12.h,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 18.h, width: 18.h).onPress(() {
Navigator.of(context).push(
CustomPageRoute(
page: MedicalFilePage(),
// page: LoginScreen(),
),
);
}),
Utils.buildSvgWithAssets(icon: AppAssets.indoor_nav_icon, height: 18.h, width: 18.h).onPress(() {
// Navigator.of(context).push(
// CustomPageRoute(
// page: MedicalFilePage(),
// // page: LoginScreen(),
// ),
// );
}),
Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 18.h, width: 18.h).onPress(() {
showCommonBottomSheetWithoutHeight(
context,
title: LocaleKeys.contactUs.tr(),
child: ContactUs(),
callBackFunc: () {},
isFullScreen: false,
);
}),
],
),
Consumer<TodoSectionViewModel>(builder: (context, todoSectionVM, child) {
return Row(
mainAxisSize: MainAxisSize.min,
spacing: 12.h,
children: [
Stack(children: [
Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 18.h, width: 18.h).onPress(() async {
if (appState.isAuthenticated) {
notificationsViewModel.setNotificationStatusID(2);
notificationsViewModel.getAllNotifications();
Navigator.of(context).push(
CustomPageRoute(
page: NotificationsListPage(),
// page: LoginScreen(),
),
);
} else {
await authVM.onLoginPressed();
}
}),
(appState.isAuthenticated && (int.parse(todoSectionVM.notificationsCount ?? "0") > 0))
? Positioned(
right: 0,
top: 0,
child: Container(
width: 8.w,
height: 8.h,
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: AppColors.primaryRedColor,
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
"",
style: TextStyle(
color: Colors.white,
fontSize: 8.f,
),
textAlign: TextAlign.center,
),
),
)
: SizedBox.shrink(),
]),
Utils.buildSvgWithAssets(icon: AppAssets.indoor_nav_icon, height: 18.h, width: 18.h).onPress(() {
openIndoorNavigationBottomSheet(context);
}),
Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 18.h, width: 18.h).onPress(() {
showCommonBottomSheetWithoutHeight(
context,
title: LocaleKeys.contactUs.tr(),
child: ContactUs(),
callBackFunc: () {},
isFullScreen: false,
);
}),
],
);
}),
],
).paddingSymmetrical(24.h, 0.h),
!appState.isAuthenticated
@ -312,15 +351,49 @@ class _LandingPageState extends State<LandingPage> {
isFromHomePage: true,
),
).paddingSymmetrical(24.h, 0.h)
: Swiper(
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 3
: myAppointmentsVM.patientAppointmentsHistoryList.length < 3
? myAppointmentsVM.patientAppointmentsHistoryList.length
: 3,
layout: SwiperLayout.STACK,
loop: true,
itemWidth: MediaQuery.of(context).size.width - 48.h,
: isTablet
? SizedBox(
height: isFoldable ? 290.h : 255.h,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: 3,
shrinkWrap: true,
padding: EdgeInsets.only(left: 16.h, right: 16.h),
itemBuilder: (context, index) {
return SizedBox(
height: 255.h,
width: 250.w,
child: getIndexSwiperCard(index),
);
// return AnimationConfiguration.staggeredList(
// position: index,
// duration: const Duration(milliseconds: 1000),
// child: SlideAnimation(
// horizontalOffset: 100.0,
// child: FadeInAnimation(
// child: SizedBox(
// height: 255.h,
// width: 250.w,
// child: getIndexSwiperCard(index),
// ),
// ),
// ),
// );
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(
width: 10.w,
),
),
)
: Swiper(
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 3
: myAppointmentsVM.patientAppointmentsHistoryList.length < 3
? myAppointmentsVM.patientAppointmentsHistoryList.length
: 3,
layout: SwiperLayout.STACK,
loop: true,
itemWidth: MediaQuery.of(context).size.width - 48.h,
indicatorLayout: PageIndicatorLayout.COLOR,
axisDirection: AxisDirection.right,
controller: _controller,
@ -331,208 +404,7 @@ class _LandingPageState extends State<LandingPage> {
builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor),
),
itemBuilder: (BuildContext context, int index) {
return (myAppointmentsVM.isPatientHasQueueAppointment && index == 0)
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
side: BorderSide(color: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), width: 2.w),
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppCustomChipWidget(
labelText: myAppointmentsVM.currentQueueStatus == 0 ? "In Queue".needTranslation : "Your Turn".needTranslation,
backgroundColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.20),
textColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus),
),
Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h),
],
),
SizedBox(height: 8.h),
"Hala ${appState.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true),
SizedBox(height: 2.h),
"Thank you for your patience, here is your queue number."
.needTranslation
.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
SizedBox(height: 8.h),
myAppointmentsVM.currentPatientQueueDetails.queueNo!.toText28(isBold: true),
SizedBox(height: 6.h),
myAppointmentsVM.patientQueueDetailsList.isNotEmpty ? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
"Serving Now: ".needTranslation.toText14(isBold: true),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
myAppointmentsVM.patientQueueDetailsList.first.queueNo!.toText12(isBold: true),
SizedBox(width: 8.w),
AppCustomChipWidget(
deleteIcon: myAppointmentsVM.patientQueueDetailsList.first.callType == 1 ? AppAssets.call_for_vitals : AppAssets.call_for_doctor,
labelText: myAppointmentsVM.patientQueueDetailsList.first.callType == 1 ? "Call for vital signs".needTranslation : "Call for Doctor".needTranslation,
iconColor: myAppointmentsVM.patientQueueDetailsList.first.callType == 1 ? AppColors.primaryRedColor : AppColors.successColor,
textColor: myAppointmentsVM.patientQueueDetailsList.first.callType == 1 ? AppColors.primaryRedColor : AppColors.successColor,
iconSize: 14.w,
backgroundColor: myAppointmentsVM.patientQueueDetailsList.first.callType == 1 ? AppColors.primaryRedColor.withValues(alpha: 0.1) : AppColors.successColor.withValues(alpha: 0.1),
labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: -2.h),
),
],
),
],
) : SizedBox(height: 12.h),
SizedBox(height: 5.h),
CustomButton(
text: Utils.getCardButtonText(myAppointmentsVM.currentQueueStatus, myAppointmentsVM.currentPatientQueueDetails.roomNo ?? ""),
onPressed: () {},
backgroundColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus),
borderColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.01),
textColor: Utils.getCardButtonTextColor(myAppointmentsVM.currentQueueStatus),
fontSize: 12.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
iconColor: AppColors.whiteColor,
iconSize: 18.h,
),
],
),
),
).onPress(() {
Navigator.of(context).push(
CustomPageRoute(
page: AppointmentQueuePage(),
),
);
})
: (immediateLiveCareVM.patientHasPendingLiveCareRequest && index == 0)
? Column(
children: [
SizedBox(height: 12.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.r,
hasShadow: true,
side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h),
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Immediate LiveCare Request".needTranslation.toText16(isBold: true),
SizedBox(height: 10.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
AppCustomChipWidget(
labelText: immediateLiveCareVM.patientLiveCareHistoryList[0].stringCallStatus,
backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20),
textColor: AppColors.alertColor,
),
SizedBox(width: 8.w),
AppCustomChipWidget(
icon: AppAssets.appointment_calendar_icon,
labelText: DateUtil.formatDateToDate(
DateUtil.convertStringToDate(immediateLiveCareVM.patientLiveCareHistoryList[0].arrivalTime), false)),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h),
// Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.cover),
],
),
SizedBox(height: 10.h),
"Hala ${appState.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true),
SizedBox(height: 8.h),
"Your turn is after ${immediateLiveCareVM.patientLiveCareHistoryList[0].patCount} patients.".needTranslation.toText14(isBold: true),
SizedBox(height: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Expected waiting time: ".needTranslation.toText12(isBold: true),
SizedBox(height: 7.h),
ValueListenableBuilder<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,
),
);
return getIndexSwiperCard(index);
},
)
: 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) {
showCommonBottomSheetWithoutHeight(
context,
@ -831,4 +985,41 @@ class _LandingPageState extends State<LandingPage> {
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:flutter/material.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hospital/hospital_selection_view_model.dart';
import 'package:hmg_patient_app_new/features/qr_parking/qr_parking_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/emergency_services_page.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculators_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart';
import 'package:hmg_patient_app_new/presentation/parking/paking_page.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
@ -126,6 +131,39 @@ class SmallServiceCard extends StatelessWidget {
case "indoor_navigation":
openIndoorNavigationBottomSheet(context);
case "search_doctor":
Navigator.of(context).push(
CustomPageRoute(
page: SearchDoctorByName(),
),
);
break;
case "health_calculators_and_converts":
Navigator.of(context).push(
CustomPageRoute(
page: HealthCalculatorsPage(type: HealthCalConEnum.calculator),
),
);
break;
case "health_converters":
Navigator.of(context).push(
CustomPageRoute(
page: HealthCalculatorsPage(type: HealthCalConEnum.converter),
),
);
break;
case "parking_guide":
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ChangeNotifierProvider(
create: (_) => getIt<QrParkingViewModel>(),
child: const ParkingPage(),
),
),
);
break;
default:
// Handle unknown service
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);
}),
),
);
}
}

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

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

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

@ -138,6 +138,6 @@ class AppRoutes {
qrParking: (context) => ChangeNotifierProvider<QrParkingViewModel>(
create: (_) => getIt<QrParkingViewModel>(),
child: const ParkingPage(),
),
),}
};
}

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

@ -89,7 +89,7 @@ class CollapsingListView extends StatelessWidget {
? Transform.flip(
flipX: appState.isArabic(),
child: IconButton(
icon: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.closeBottomNav : AppAssets.arrow_back, width: 24.h, height: 24.h),
icon: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.closeBottomNav : AppAssets.forward_top_nav_icon, width: 24.h, height: 24.h),
padding: EdgeInsets.only(left: 12),
onPressed: () {
if (leadingCallback != null) {

Loading…
Cancel
Save