appointment reminder & weather

pull/236/head
aamir-csol 2 days ago
parent 7bf69c52f0
commit 2b0f8b45b0

@ -39,7 +39,7 @@
<uses-permission <uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"
tools:node="remove" /> tools:node="remove" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" tools:node="remove" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- Gallery/Photo permissions for Android 13+ (API 33+) --> <!-- Gallery/Photo permissions for Android 13+ (API 33+) -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
@ -52,7 +52,6 @@
<uses-permission <uses-permission
android:name="android.permission.ACCESS_BACKGROUND_LOCATION" android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
tools:node="remove" /> <!-- <uses-permission android:name="android.permission.INTERNET" /> --> tools:node="remove" /> <!-- <uses-permission android:name="android.permission.INTERNET" /> -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" tools:node="remove" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" /> <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

@ -0,0 +1,9 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.00439 1.5C9.41861 1.5 9.75439 1.83579 9.75439 2.25V4.50003L10.1913 4.50001C10.323 4.49992 10.4848 4.4998 10.6171 4.51636L10.6196 4.51667C10.7144 4.5285 11.1465 4.58237 11.3522 5.00657C11.5585 5.43169 11.332 5.80679 11.283 5.88795L11.0287 6.23932C10.8077 6.52119 10.5034 6.90703 10.2181 7.20028C10.0757 7.34661 9.91277 7.49754 9.73959 7.61668C9.58565 7.72258 9.323 7.87503 9 7.87503C8.677 7.87503 8.41436 7.72258 8.26041 7.61668C8.08723 7.49754 7.92428 7.34661 7.78191 7.20028C7.49659 6.90703 7.19231 6.52118 6.97134 6.23931L6.71698 5.88795C6.66799 5.80679 6.44154 5.43169 6.64776 5.00657C6.85353 4.58237 7.28556 4.5285 7.38039 4.51667L7.3829 4.51636C7.51523 4.49981 7.67697 4.49992 7.8087 4.50001L8.25439 4.50003V2.25C8.25439 1.83579 8.59018 1.5 9.00439 1.5Z" fill="#2E3039"/>
<path d="M14.3028 8.50787C14.5957 8.21498 14.5957 7.7401 14.3028 7.44721C14.0099 7.15432 13.535 7.15432 13.2421 7.44721L12.4466 8.2427C12.1537 8.5356 12.1537 9.01047 12.4466 9.30336C12.7395 9.59626 13.2144 9.59626 13.5073 9.30336L14.3028 8.50787Z" fill="#2E3039"/>
<path d="M4.75778 7.44767C4.46489 7.15478 3.99002 7.15478 3.69712 7.44767C3.40423 7.74057 3.40423 8.21544 3.69712 8.50833L4.49262 9.30383C4.78551 9.59672 5.26039 9.59672 5.55328 9.30383C5.84617 9.01094 5.84617 8.53606 5.55328 8.24317L4.75778 7.44767Z" fill="#2E3039"/>
<path d="M2.25 12.0005C1.83579 12.0005 1.5 12.3363 1.5 12.7505C1.5 13.1648 1.83579 13.5005 2.25 13.5005H3.375C3.78921 13.5005 4.125 13.1648 4.125 12.7505C4.125 12.3363 3.78921 12.0005 3.375 12.0005H2.25Z" fill="#2E3039"/>
<path d="M14.625 12.0005C14.2108 12.0005 13.875 12.3363 13.875 12.7505C13.875 13.1648 14.2108 13.5005 14.625 13.5005H15.75C16.1642 13.5005 16.5 13.1648 16.5 12.7505C16.5 12.3363 16.1642 12.0005 15.75 12.0005H14.625Z" fill="#2E3039"/>
<path d="M16.5 15.75C16.5 16.1642 16.1642 16.5 15.75 16.5L2.25 16.5C1.83579 16.5 1.5 16.1642 1.5 15.75C1.5 15.3358 1.83579 15 2.25 15L15.75 15C16.1642 15 16.5 15.3358 16.5 15.75Z" fill="#2E3039"/>
<path d="M9 9C6.92893 9 5.25 10.6789 5.25 12.75C5.25 13.1642 5.58579 13.5 6 13.5H12C12.4142 13.5 12.75 13.1642 12.75 12.75C12.75 10.6789 11.0711 9 9 9Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@ -0,0 +1,9 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.00439 7.87503C9.41861 7.87503 9.75439 7.53924 9.75439 7.12503L9.75439 4.875L10.1913 4.87501C10.323 4.87511 10.4848 4.87522 10.6171 4.85867L10.6196 4.85836C10.7144 4.84653 11.1465 4.79266 11.3522 4.36846C11.5585 3.94334 11.332 3.56824 11.283 3.48708L11.0287 3.13571C10.8077 2.85384 10.5034 2.468 10.2181 2.17474C10.0757 2.02841 9.91277 1.87749 9.73959 1.75835C9.58565 1.65245 9.323 1.5 9 1.5C8.677 1.5 8.41436 1.65245 8.26041 1.75835C8.08723 1.87749 7.92428 2.02841 7.78191 2.17474C7.49658 2.468 7.19231 2.85385 6.97134 3.13572L6.71698 3.48708C6.66799 3.56823 6.44154 3.94334 6.64776 4.36846C6.85353 4.79265 7.28556 4.84653 7.38039 4.85836L7.38291 4.85867C7.51523 4.87522 7.67697 4.87511 7.8087 4.87501L8.25439 4.875L8.25439 7.12503C8.25439 7.53924 8.59018 7.87503 9.00439 7.87503Z" fill="#2E3039"/>
<path d="M14.3028 8.5079C14.5957 8.215 14.5957 7.74013 14.3028 7.44724C14.0099 7.15434 13.535 7.15434 13.2421 7.44724L12.4466 8.24273C12.1537 8.53563 12.1537 9.0105 12.4466 9.30339C12.7395 9.59629 13.2144 9.59629 13.5073 9.30339L14.3028 8.5079Z" fill="#2E3039"/>
<path d="M4.75778 7.4477C4.46489 7.15481 3.99002 7.15481 3.69712 7.4477C3.40423 7.74059 3.40423 8.21547 3.69712 8.50836L4.49262 9.30386C4.78551 9.59675 5.26039 9.59675 5.55328 9.30386C5.84617 9.01096 5.84617 8.53609 5.55328 8.2432L4.75778 7.4477Z" fill="#2E3039"/>
<path d="M2.25 12.0006C1.83579 12.0006 1.5 12.3364 1.5 12.7506C1.5 13.1648 1.83579 13.5006 2.25 13.5006H3.375C3.78921 13.5006 4.125 13.1648 4.125 12.7506C4.125 12.3364 3.78921 12.0006 3.375 12.0006H2.25Z" fill="#2E3039"/>
<path d="M14.625 12.0006C14.2108 12.0006 13.875 12.3364 13.875 12.7506C13.875 13.1648 14.2108 13.5006 14.625 13.5006H15.75C16.1642 13.5006 16.5 13.1648 16.5 12.7506C16.5 12.3364 16.1642 12.0006 15.75 12.0006H14.625Z" fill="#2E3039"/>
<path d="M16.5 15.75C16.5 16.1642 16.1642 16.5 15.75 16.5L2.25 16.5C1.83579 16.5 1.5 16.1642 1.5 15.75C1.5 15.3358 1.83579 15 2.25 15L15.75 15C16.1642 15 16.5 15.3358 16.5 15.75Z" fill="#2E3039"/>
<path d="M9 9.00003C6.92893 9.00003 5.25 10.679 5.25 12.75C5.25 13.1642 5.58579 13.5 6 13.5H12C12.4142 13.5 12.75 13.1642 12.75 12.75C12.75 10.679 11.0711 9.00003 9 9.00003Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@ -64,6 +64,8 @@ import 'package:hmg_patient_app_new/features/todo_section/todo_section_repo.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/features/water_monitor/water_monitor_repo.dart'; import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_repo.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/features/weather/weather_repo.dart';
import 'package:hmg_patient_app_new/features/weather/weather_view_model.dart';
import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_view_model.dart'; import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_view_model.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart'; import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart'; import 'package:hmg_patient_app_new/services/cache_service.dart';
@ -164,6 +166,7 @@ class AppDependencies {
getIt.registerLazySingleton<SymptomsCheckerRepo>(() => SymptomsCheckerRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<SymptomsCheckerRepo>(() => SymptomsCheckerRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<BloodDonationRepo>(() => BloodDonationRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<BloodDonationRepo>(() => BloodDonationRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<WaterMonitorRepo>(() => WaterMonitorRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<WaterMonitorRepo>(() => WaterMonitorRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<WeatherRepo>(() => WeatherRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MyInvoicesRepo>(() => MyInvoicesRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<MyInvoicesRepo>(() => MyInvoicesRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<HealthTrackersRepo>(() => HealthTrackersRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<HealthTrackersRepo>(() => HealthTrackersRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MonthlyReportRepo>(() => MonthlyReportRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<MonthlyReportRepo>(() => MonthlyReportRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
@ -299,6 +302,8 @@ class AppDependencies {
getIt.registerLazySingleton<WaterMonitorViewModel>(() => WaterMonitorViewModel(waterMonitorRepo: getIt(), errorHandlerService: getIt())); getIt.registerLazySingleton<WaterMonitorViewModel>(() => WaterMonitorViewModel(waterMonitorRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<WeatherMonitorViewModel>(() => WeatherMonitorViewModel(weatherRepo: getIt(), errorHandlerService: getIt(), locationUtils: 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()));

@ -41,6 +41,7 @@ class MedicalFileViewModel extends ChangeNotifier {
List<SickLeaveList> patientSickLeavesViewList = []; List<SickLeaveList> patientSickLeavesViewList = [];
bool isSickLeavesSortByClinic = true; bool isSickLeavesSortByClinic = true;
// bool isSickLeavesDataNeedsReloading = true; // bool isSickLeavesDataNeedsReloading = true;
List<GetAllergiesResponseModel> patientAllergiesList = []; List<GetAllergiesResponseModel> patientAllergiesList = [];
@ -165,13 +166,13 @@ class MedicalFileViewModel extends ChangeNotifier {
setIsPatientSickLeaveListLoading(bool val) { setIsPatientSickLeaveListLoading(bool val) {
// if (val && isSickLeavesDataNeedsReloading) { // if (val && isSickLeavesDataNeedsReloading) {
patientSickLeaveList.clear(); patientSickLeaveList.clear();
patientSickLeavesByClinic.clear(); patientSickLeavesByClinic.clear();
patientSickLeavesByHospital.clear(); patientSickLeavesByHospital.clear();
patientSickLeavesViewList.clear(); patientSickLeavesViewList.clear();
patientSickLeavePDFBase64 = ""; patientSickLeavePDFBase64 = "";
isSickLeavesSortByClinic = true; isSickLeavesSortByClinic = true;
isPatientSickLeaveListLoading = val; isPatientSickLeaveListLoading = val;
// } // }
notifyListeners(); notifyListeners();
} }
@ -774,4 +775,6 @@ class MedicalFileViewModel extends ChangeNotifier {
}, },
); );
} }
} }

@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.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/utils/calender_utils_new.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.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/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
@ -1109,4 +1110,108 @@ class MyAppointmentsViewModel extends ChangeNotifier {
}, },
); );
} }
/// Creates a reminder for the appointment
/// Shows a bottom sheet to select reminder time and creates calendar event
Future<void> createReminder({
required BuildContext context,
required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
Function(bool)? onSuccess,
Function(String)? onError,
}) async {
try {
CalenderUtilsNew calender = CalenderUtilsNew.instance;
DateTime endDate = DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate);
BottomSheetUtils().showReminderBottomSheet(
context,
endDate,
patientAppointmentHistoryResponseModel.doctorNameObj ?? "",
"${patientAppointmentHistoryResponseModel.appointmentNo}",
"",
"",
title: "Appointment with ${patientAppointmentHistoryResponseModel.doctorNameObj}",
description: "${patientAppointmentHistoryResponseModel.doctorNameObj} will be having an appointment on ${patientAppointmentHistoryResponseModel.appointmentDate}",
onSuccess: () {
setAppointmentReminder(true, patientAppointmentHistoryResponseModel);
if (onSuccess != null) {
onSuccess(true);
}
},
isMultiAllowed: true,
onMultiDateSuccess: (int selectedIndex) async {
bool isEventAddedOrRemoved = await calender.createOrUpdateEvent(
title: "Appointment Reminder with ${patientAppointmentHistoryResponseModel.doctorNameObj} on ${DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)}, Appointment #${patientAppointmentHistoryResponseModel.appointmentNo}",
description: "Appointment Reminder with ${patientAppointmentHistoryResponseModel.doctorNameObj} in ${patientAppointmentHistoryResponseModel.projectName}",
scheduleDateTime: DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate),
eventId: "${patientAppointmentHistoryResponseModel.appointmentNo}",
location: '',
reminderMinutes: selectedIndex,
);
setAppointmentReminder(isEventAddedOrRemoved, patientAppointmentHistoryResponseModel);
if (onSuccess != null) {
onSuccess(isEventAddedOrRemoved);
}
},
);
} catch (e) {
if (onError != null) {
onError(e.toString());
}
}
}
/// Deletes the reminder for the appointment
/// Removes the calendar event associated with the appointment
Future<void> deleteReminder({
required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
Function(bool)? onSuccess,
Function(String)? onError,
}) async {
try {
CalenderUtilsNew calender = CalenderUtilsNew.instance;
bool isEventRemoved = await calender.checkAndRemove(
id: "${patientAppointmentHistoryResponseModel.appointmentNo}",
);
// Update the reminder status (inverted because we're removing)
setAppointmentReminder(!isEventRemoved, patientAppointmentHistoryResponseModel);
if (onSuccess != null) {
onSuccess(isEventRemoved);
}
} catch (e) {
if (onError != null) {
onError(e.toString());
}
}
}
/// Toggle reminder for an appointment
/// Creates or deletes reminder based on the current state
Future<void> toggleReminder({
required BuildContext context,
required bool shouldCreateReminder,
required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
Function(bool)? onSuccess,
Function(String)? onError,
}) async {
if (shouldCreateReminder) {
await createReminder(
context: context,
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
onSuccess: onSuccess,
onError: onError,
);
} else {
await deleteReminder(
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
onSuccess: onSuccess,
onError: onError,
);
}
}
} }

@ -0,0 +1,257 @@
import 'dart:convert';
class GetCityInfoList {
CategoriesNames? categoriesNames;
String? category;
String? categoryValue;
int? cityId;
String? cityName;
String? cityNameN;
ColorName? colorName;
String? createdOn;
String? id;
int? iniceId;
bool? isOrderEmpty;
bool? isValuesReversed;
bool? language;
double? latitude;
double? longitude;
String? name;
int? orderNum;
double? temperature;
String? value;
GetCityInfoList({
this.categoriesNames,
this.category,
this.categoryValue,
this.cityId,
this.cityName,
this.cityNameN,
this.colorName,
this.createdOn,
this.id,
this.iniceId,
this.isOrderEmpty,
this.isValuesReversed,
this.language,
this.latitude,
this.longitude,
this.name,
this.orderNum,
this.temperature,
this.value,
});
factory GetCityInfoList.fromRawJson(String str) => GetCityInfoList.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory GetCityInfoList.fromJson(Map<String, dynamic> json) {
ColorName? parseColorName(dynamic value) {
if (value == null) return null;
try {
return colorNameValues.map[value.toString()];
} catch (e) {
print("Unknown color name: $value");
return null;
}
}
CategoriesNames? parseCategoriesNames(dynamic value) {
if (value == null) return null;
try {
return CategoriesNames.fromJson(value as Map<String, dynamic>);
} catch (e) {
print("Error parsing CategoriesNames: $e");
return null;
}
}
return GetCityInfoList(
categoriesNames: parseCategoriesNames(json["CategoriesNames"]),
category: json["Category"],
categoryValue: json["CategoryValue"],
cityId: json["CityID"],
cityName: json["CityName"],
cityNameN: json["CityNameN"],
colorName: parseColorName(json["ColorName"]),
createdOn: json["CreatedOn"],
id: json["ID"],
iniceId: json["IniceID"],
isOrderEmpty: json["IsOrderEmpty"],
isValuesReversed: json["IsValuesReversed"],
language: json["Language"],
latitude: json["Latitude"]?.toDouble(),
longitude: json["Longitude"]?.toDouble(),
name: json["Name"],
orderNum: json["OrderNum"],
temperature: json["Temperature"]?.toDouble(),
value: json["Value"],
);
}
Map<String, dynamic> toJson() => {
"CategoriesNames": categoriesNames?.toJson(),
"Category": category,
"CategoryValue": categoryValue,
"CityID": cityId,
"CityName": cityName,
"CityNameN": cityNameN,
"ColorName": colorNameValues.reverse[colorName],
"CreatedOn": createdOn,
"ID": id,
"IniceID": iniceId,
"IsOrderEmpty": isOrderEmpty,
"IsValuesReversed": isValuesReversed,
"Language": language,
"Latitude": latitude,
"Longitude": longitude,
"Name": name,
"OrderNum": orderNum,
"Temperature": temperature,
"Value": value,
};
}
class CategoriesNames {
Category1? category1;
Category2? category2;
Category3? category3;
Category4? category4;
Category5? category5;
CategoriesNames({
this.category1,
this.category2,
this.category3,
this.category4,
this.category5,
});
factory CategoriesNames.fromRawJson(String str) => CategoriesNames.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory CategoriesNames.fromJson(Map<String, dynamic> json) {
T? parseEnum<T>(dynamic value, EnumValues<T> enumValues, String enumName) {
if (value == null) return null;
try {
return enumValues.map[value.toString()];
} catch (e) {
print("Unknown $enumName value: $value");
return null;
}
}
return CategoriesNames(
category1: parseEnum(json["Category1"], category1Values, "Category1"),
category2: parseEnum(json["Category2"], category2Values, "Category2"),
category3: parseEnum(json["Category3"], category3Values, "Category3"),
category4: parseEnum(json["Category4"], category4Values, "Category4"),
category5: parseEnum(json["Category5"], category5Values, "Category5"),
);
}
Map<String, dynamic> toJson() => {
"Category1": category1Values.reverse[category1],
"Category2": category2Values.reverse[category2],
"Category3": category3Values.reverse[category3],
"Category4": category4Values.reverse[category4],
"Category5": category5Values.reverse[category5],
};
}
enum Category1 {
AT_EXTREME_RISK,
CATEGORY1_EXTREME,
EXTREME,
POOR
}
final category1Values = EnumValues({
"At Extreme Risk": Category1.AT_EXTREME_RISK,
"Extreme\u000d\n": Category1.CATEGORY1_EXTREME,
"Extreme": Category1.EXTREME,
"Poor": Category1.POOR
});
enum Category2 {
AT_HIGH_RISK,
CATEGORY2_VERY_HIGH,
FAIR,
VERY_HIGH
}
final category2Values = EnumValues({
"At High Risk": Category2.AT_HIGH_RISK,
"Very High\u000d\n": Category2.CATEGORY2_VERY_HIGH,
"Fair": Category2.FAIR,
"Very High": Category2.VERY_HIGH
});
enum Category3 {
AT_RISK,
CATEGORY3_HIGH,
GOOD,
HIGH
}
final category3Values = EnumValues({
"At Risk": Category3.AT_RISK,
"High\u000d\n": Category3.CATEGORY3_HIGH,
"Good": Category3.GOOD,
"High": Category3.HIGH
});
enum Category4 {
CATEGORY4_MODERATE,
MODERATE,
NEUTRAL,
VERY_GOOD
}
final category4Values = EnumValues({
"Moderate\u000d\n": Category4.CATEGORY4_MODERATE,
"Moderate": Category4.MODERATE,
"Neutral": Category4.NEUTRAL,
"Very Good": Category4.VERY_GOOD
});
enum Category5 {
BENEFICIAL,
CATEGORY5_LOW,
EXCELLENT,
LOW
}
final category5Values = EnumValues({
"Beneficial": Category5.BENEFICIAL,
"Low\u000d\n": Category5.CATEGORY5_LOW,
"Excellent": Category5.EXCELLENT,
"Low": Category5.LOW
});
enum ColorName {
GREEN,
ORANGE,
RED
}
final colorNameValues = EnumValues({
"green": ColorName.GREEN,
"orange": ColorName.ORANGE,
"red": ColorName.RED
});
class EnumValues<T> {
Map<String, T> map;
late Map<T, String> reverseMap;
EnumValues(this.map);
Map<T, String> get reverse {
reverseMap = map.map((k, v) => MapEntry(v, k));
return reverseMap;
}
}

@ -0,0 +1,72 @@
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/core/location_util.dart';
import 'package:hmg_patient_app_new/features/weather/models/waether_cities_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class WeatherRepo {
Future<Either<Failure, GenericApiModel<List<GetCityInfoList>>>> getCityInfo();
}
class WeatherRepoImp implements WeatherRepo {
final ApiClient apiClient;
final LoggerService loggerService;
WeatherRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<GetCityInfoList>>>> getCityInfo() async {
Map<String, dynamic> request = {};
try {
GenericApiModel<List<GetCityInfoList>>? apiResponse;
Failure? failure;
await apiClient.post(
WEATHER_INDICATOR,
body: request,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
loggerService.logInfo("Weather API Response: $response");
final list = response['GetCityInfo_List'];
if (list == null || list.isEmpty) {
loggerService.logInfo("Weather API returned empty or null list");
// Return empty list if no city info
apiResponse = GenericApiModel<List<GetCityInfoList>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: [],
);
return;
}
loggerService.logInfo("Parsing ${list.length} city info items");
final cityInfoList = list.map((item) => GetCityInfoList.fromJson(item as Map<String, dynamic>)).toList().cast<GetCityInfoList>();
apiResponse = GenericApiModel<List<GetCityInfoList>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: cityInfoList,
);
} catch (e) {
loggerService.logError("Error parsing weather data: $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,55 @@
import 'package:flutter/cupertino.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/features/weather/models/waether_cities_model.dart';
import 'package:hmg_patient_app_new/features/weather/weather_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class WeatherMonitorViewModel extends ChangeNotifier {
WeatherRepo weatherRepo;
ErrorHandlerService errorHandlerService;
LocationUtils locationUtils;
WeatherMonitorViewModel({required this.weatherRepo, required this.errorHandlerService, required this.locationUtils});
final List<GetCityInfoList> _cityInfoList = [];
bool isLoading = false;
List<GetCityInfoList> get cityInfoList => _cityInfoList;
Future<void> fetchCityInfoList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
isLoading = true;
_cityInfoList.clear();
locationUtils.getLocation(
isShowConfirmDialog: true,
onSuccess: (position) async {
final result = await weatherRepo.getCityInfo();
result.fold(
(failure) async {
isLoading = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
isLoading = false;
if (apiResponse.messageStatus == 2) {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? "Unknown error");
}
} else if (apiResponse.messageStatus == 1) {
final cities = apiResponse.data ?? [];
_cityInfoList.addAll(cities);
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
});
}
}

@ -10,6 +10,7 @@ import 'package:flutter/services.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/dependencies.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/weather/weather_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/features/ask_doctor/ask_doctor_view_model.dart'; import 'package:hmg_patient_app_new/features/ask_doctor/ask_doctor_view_model.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
@ -242,6 +243,9 @@ void main() async {
), ),
ChangeNotifierProvider<ServicesPriceListViewModel>( ChangeNotifierProvider<ServicesPriceListViewModel>(
create: (_) => getIt.get<ServicesPriceListViewModel>(), create: (_) => getIt.get<ServicesPriceListViewModel>(),
) ,
ChangeNotifierProvider<WeatherMonitorViewModel>(
create: (_) => getIt.get<WeatherMonitorViewModel>(),
) )
], child: MyApp()), ], child: MyApp()),
), // Wrap your app ), // Wrap your app

@ -7,7 +7,6 @@ import 'package:hmg_patient_app_new/core/api_consts.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_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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/calender_utils_new.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/date_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/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
@ -196,11 +195,13 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
}, },
onError: (err) { onError: (err) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(context,
context,
child: Utils.getErrorWidget(loadingText: err.toString()), child: Utils.getErrorWidget(loadingText: err.toString()),
callBackFunc: () {}, callBackFunc: () {},
title: "", isCloseButtonVisible: true, isDismissible: false, isFullScreen: false); title: "",
isCloseButtonVisible: true,
isDismissible: false,
isFullScreen: false);
}); });
}), }),
callBackFunc: () {}, callBackFunc: () {},
@ -359,22 +360,24 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
], ],
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
bookAppointmentsViewModel.appointmentNearestGateResponseModel != null ? Wrap( bookAppointmentsViewModel.appointmentNearestGateResponseModel != null
direction: Axis.horizontal, ? Wrap(
spacing: 8.w, direction: Axis.horizontal,
runSpacing: 8.h, spacing: 8.w,
children: [ runSpacing: 8.h,
AppCustomChipWidget( children: [
labelText: bookAppointmentsVM.isAppointmentNearestGateLoading AppCustomChipWidget(
? "Floor: Ground Floor" labelText: bookAppointmentsVM.isAppointmentNearestGateLoading
? "Floor: Ground Floor"
: "${LocaleKeys.floor.tr(context: context)}: ${getIt.get<AppState>().isArabic() ? bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocationN : bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocation}", : "${LocaleKeys.floor.tr(context: context)}: ${getIt.get<AppState>().isArabic() ? bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocationN : bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocation}",
).toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), ).toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading),
AppCustomChipWidget( AppCustomChipWidget(
labelText: labelText:
"${LocaleKeys.nearestGate.tr(context: context)}: ${getIt.get<AppState>().isArabic() ? bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumberN : bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumber}") "${LocaleKeys.nearestGate.tr(context: context)}: ${getIt.get<AppState>().isArabic() ? bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumberN : bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumber}")
.toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), .toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading),
], ],
) : SizedBox.shrink(), )
: SizedBox.shrink(),
], ],
), ),
), ),
@ -405,53 +408,40 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
// activeTrackColor: AppColors.successColor.withValues(alpha: .15), // activeTrackColor: AppColors.successColor.withValues(alpha: .15),
value: widget.patientAppointmentHistoryResponseModel.hasReminder!, value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
onChanged: (newValue) async { onChanged: (newValue) async {
CalenderUtilsNew calender = CalenderUtilsNew.instance; await myAppointmentsViewModel.toggleReminder(
bool isEventAddedOrRemoved = false; context: context,
if(newValue == true){ shouldCreateReminder: newValue,
DateTime startDate = DateTime.now(); patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
DateTime endDate = DateUtil.convertStringToDate(widget onSuccess: (result) {
.patientAppointmentHistoryResponseModel.appointmentDate); showCommonBottomSheet(
BottomSheetUtils().showReminderBottomSheet( context,
context, child: Utils.getSuccessWidget(
endDate, loadingText: newValue ? LocaleKeys.reminderSuccess.tr(context: context) : LocaleKeys.reminderCancelSuccess.tr(context: context),
widget.patientAppointmentHistoryResponseModel.doctorNameObj??"", ),
"${widget.patientAppointmentHistoryResponseModel.appointmentNo}"??"", callBackFunc: (str) {},
"", title: "",
"", isCloseButtonVisible: false,
title: "Appointment with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}", isDismissible: true,
description: isFullScreen: false,
"${widget.patientAppointmentHistoryResponseModel.doctorNameObj} will be having an appointment on ${widget.patientAppointmentHistoryResponseModel.appointmentDate}", isAutoDismiss: true,
onSuccess: () { height: ResponsiveExtension.screenHeight * 0.3,
setState(() { );
myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel); },
}); onError: (error) {
}, setState(() {
isMultiAllowed: true, // Revert the switch if there was an error
onMultiDateSuccess: (int selectedIndex) async { widget.patientAppointmentHistoryResponseModel.hasReminder = !newValue;
isEventAddedOrRemoved = await calender.createOrUpdateEvent( });
title: showCommonBottomSheetWithoutHeight(
"Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} on ${DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate)}, Appointment #${widget.patientAppointmentHistoryResponseModel.appointmentNo}", context,
description: title: LocaleKeys.notice.tr(context: context),
"Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} in ${widget.patientAppointmentHistoryResponseModel.projectName}", child: Utils.getErrorWidget(loadingText: error),
scheduleDateTime: DateUtil.convertStringToDate(widget callBackFunc: () {},
.patientAppointmentHistoryResponseModel.appointmentDate), isFullScreen: false,
eventId: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}", isCloseButtonVisible: true,
location: '', );
reminderMinutes: selectedIndex },
); );
setState(() {
myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
});
},
);
}else {
isEventAddedOrRemoved = await calender.checkAndRemove( id:"${widget.patientAppointmentHistoryResponseModel.appointmentNo}", );
setState(() {
myAppointmentsViewModel.setAppointmentReminder(!isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
});
}
}, },
), ),
], ],
@ -471,106 +461,116 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
), ),
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
shrinkWrap: true, shrinkWrap: true,
children: [ children: [
MedicalFileCard( MedicalFileCard(
label: LocaleKeys.labResults.tr(context: context), label: LocaleKeys.labResults.tr(context: context),
textColor: AppColors.blackColor, textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.lab_result_icon, svgIcon: AppAssets.lab_result_icon,
isLargeText: true, isLargeText: true,
iconSize: 36.w, iconSize: 36.w,
).onPress(() async { ).onPress(() async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingLabResults.tr(context: context)); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingLabResults.tr(context: context));
await labViewModel.getLabResultsByAppointmentNo( await labViewModel.getLabResultsByAppointmentNo(
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo, appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo,
projectID: widget.patientAppointmentHistoryResponseModel.projectID, projectID: widget.patientAppointmentHistoryResponseModel.projectID,
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
doctorID: widget.patientAppointmentHistoryResponseModel.doctorID, doctorID: widget.patientAppointmentHistoryResponseModel.doctorID,
doctorName: widget.patientAppointmentHistoryResponseModel.doctorNameObj!, doctorName: widget.patientAppointmentHistoryResponseModel.doctorNameObj!,
clinicName: widget.patientAppointmentHistoryResponseModel.clinicName!, clinicName: widget.patientAppointmentHistoryResponseModel.clinicName!,
projectName: widget.patientAppointmentHistoryResponseModel.projectName!, projectName: widget.patientAppointmentHistoryResponseModel.projectName!,
appointmentDate: widget.patientAppointmentHistoryResponseModel.appointmentDate!, appointmentDate: widget.patientAppointmentHistoryResponseModel.appointmentDate!,
onSuccess: (val) { onSuccess: (val) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
}, },
onError: (err) { onError: (err) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: err), child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {}, callBackFunc: () {},
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
);
},
);
}),
MedicalFileCard(
label: "${LocaleKeys.radiology.tr(context: context)} ${LocaleKeys.radiologySubtitle.tr(context: context)}",
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.radiology_icon,
isLargeText: true,
iconSize: 36.w,
).onPress(() async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingRadiologyResults.tr(context: context));
await radiologyViewModel.getPatientRadiologyOrdersByAppointment(
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
}),
MedicalFileCard(
label: LocaleKeys.prescriptions.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.prescription_item_icon,
isLargeText: true,
iconSize: 36.w,
).onPress(() async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingAppointmentPrescriptions.tr(context: context));
await prescriptionsViewModel.getPrescriptionDetails(
getPrescriptionRequestModel(),
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
if (val.data.isNotEmpty) {
PatientPrescriptionsResponseModel patientPrescriptionsResponseModel = PatientPrescriptionsResponseModel(
doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL,
doctorName: widget.patientAppointmentHistoryResponseModel.doctorNameObj,
appointmentDate: widget.patientAppointmentHistoryResponseModel.appointmentDate,
clinicDescription: widget.patientAppointmentHistoryResponseModel.clinicName,
decimalDoctorRate: widget.patientAppointmentHistoryResponseModel.decimalDoctorRate,
name: widget.patientAppointmentHistoryResponseModel.projectName,
isHomeMedicineDeliverySupported: val.data.first.isHomeMedicineDeliverySupported ?? false,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
doctorID: widget.patientAppointmentHistoryResponseModel.doctorID,
setupID: widget.patientAppointmentHistoryResponseModel.setupID,
isInOutPatient: widget.patientAppointmentHistoryResponseModel.isInOutPatient,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo,
episodeID: widget.patientAppointmentHistoryResponseModel.episodeID,
dischargeNo: 0,
); );
Navigator.of(context).push( },
CustomPageRoute( );
page: PrescriptionDetailPage( }),
isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel), MedicalFileCard(
), label: "${LocaleKeys.radiology.tr(context: context)} ${LocaleKeys.radiologySubtitle.tr(context: context)}",
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.radiology_icon,
isLargeText: true,
iconSize: 36.w,
).onPress(() async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingRadiologyResults.tr(context: context));
await radiologyViewModel.getPatientRadiologyOrdersByAppointment(
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
); );
} else { },
);
}),
MedicalFileCard(
label: LocaleKeys.prescriptions.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.prescription_item_icon,
isLargeText: true,
iconSize: 36.w,
).onPress(() async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingAppointmentPrescriptions.tr(context: context));
await prescriptionsViewModel.getPrescriptionDetails(
getPrescriptionRequestModel(),
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
if (val.data.isNotEmpty) {
PatientPrescriptionsResponseModel patientPrescriptionsResponseModel = PatientPrescriptionsResponseModel(
doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL,
doctorName: widget.patientAppointmentHistoryResponseModel.doctorNameObj,
appointmentDate: widget.patientAppointmentHistoryResponseModel.appointmentDate,
clinicDescription: widget.patientAppointmentHistoryResponseModel.clinicName,
decimalDoctorRate: widget.patientAppointmentHistoryResponseModel.decimalDoctorRate,
name: widget.patientAppointmentHistoryResponseModel.projectName,
isHomeMedicineDeliverySupported: val.data.first.isHomeMedicineDeliverySupported ?? false,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
doctorID: widget.patientAppointmentHistoryResponseModel.doctorID,
setupID: widget.patientAppointmentHistoryResponseModel.setupID,
isInOutPatient: widget.patientAppointmentHistoryResponseModel.isInOutPatient,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo,
episodeID: widget.patientAppointmentHistoryResponseModel.episodeID,
dischargeNo: 0,
);
Navigator.of(context).push(
CustomPageRoute(
page: PrescriptionDetailPage(isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel),
),
);
} else {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.noPrescriptionsForAppointment.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.noPrescriptionsForAppointment.tr(context: context)), child: Utils.getErrorWidget(loadingText: LocaleKeys.noPrescriptionsForAppointment.tr(context: context)),
@ -578,40 +578,29 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
); );
} },
}, );
onError: (err) { // Navigator.of(context).push(
LoaderBottomSheet.hideLoader(); // CustomPageRoute(
showCommonBottomSheetWithoutHeight( // page: VaccineListPage(),
context, // ),
child: Utils.getErrorWidget(loadingText: LocaleKeys.noPrescriptionsForAppointment.tr(context: context)), // );
callBackFunc: () {}, }),
isFullScreen: false, MedicalFileCard(
isCloseButtonVisible: true, label: LocaleKeys.doctorResponses.tr(context: context),
); textColor: AppColors.blackColor,
}, backgroundColor: AppColors.whiteColor,
); svgIcon: AppAssets.ask_doctor_medical_file_icon,
// Navigator.of(context).push( isLargeText: true,
// CustomPageRoute( iconSize: 36.w,
// page: VaccineListPage(), ).onPress(() {
// ), getIt.get<AskDoctorViewModel>().initAskDoctorViewModel();
// ); getIt.get<AskDoctorViewModel>().getDoctorResponses();
}), Navigator.of(context).push(
MedicalFileCard( CustomPageRoute(
label: LocaleKeys.doctorResponses.tr(context: context), page: DoctorResponsePage(),
textColor: AppColors.blackColor, ),
backgroundColor: AppColors.whiteColor, );
svgIcon: AppAssets.ask_doctor_medical_file_icon,
isLargeText: true,
iconSize: 36.w,
).onPress(() {
getIt.get<AskDoctorViewModel>().initAskDoctorViewModel();
getIt.get<AskDoctorViewModel>().getDoctorResponses();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorResponsePage(),
),
);
}), }),
myAppointmentsVM.isAppointmentRatedResponseLoading myAppointmentsVM.isAppointmentRatedResponseLoading
? MedicalFileCard( ? MedicalFileCard(
@ -882,8 +871,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 || if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 || widget.patientAppointmentHistoryResponseModel.nextAction == 20)
widget.patientAppointmentHistoryResponseModel.nextAction == 20)
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -891,10 +879,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
LocaleKeys.amountBeforeTax.tr(context: context).toText18(isBold: true), LocaleKeys.amountBeforeTax.tr(context: context).toText18(isBold: true),
Utils.getPaymentAmountWithSymbol( Utils.getPaymentAmountWithSymbol(widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13,
widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true),
AppColors.blackColor,
13,
isSaudiCurrency: true), isSaudiCurrency: true),
], ],
), ),
@ -902,9 +887,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(child: LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor)),
child: LocaleKeys.upcomingPaymentNow
.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor)),
"VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})".toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -0.64), "VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})".toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -0.64),
], ],
), ),
@ -919,10 +902,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Utils.getPaymentAmountWithSymbol( Utils.getPaymentAmountWithSymbol(widget.patientAppointmentHistoryResponseModel.patientShareWithTax!.toString().toText24(isBold: true), AppColors.blackColor, 17,
widget.patientAppointmentHistoryResponseModel.patientShareWithTax!.toString().toText24(isBold: true),
AppColors.blackColor,
17,
isSaudiCurrency: true), isSaudiCurrency: true),
], ],
), ),
@ -945,11 +925,11 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
borderColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor,
textColor: Colors.white, textColor: Colors.white,
fontSize: 16.f, fontSize: 16.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
borderRadius: 12.r, borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w), padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 50.h, height: 50.h,
icon: AppAssets.add_icon, icon: AppAssets.add_icon,
iconColor: Colors.white, iconColor: Colors.white,
iconSize: 18.h, iconSize: 18.h,
).paddingSymmetrical(16.h, 24.h) ).paddingSymmetrical(16.h, 24.h)
@ -959,8 +939,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction); handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction);
}, },
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction), backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction),
borderColor: borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: widget.patientAppointmentHistoryResponseModel.nextAction == 15 ? AppColors.textColor : Colors.white, textColor: widget.patientAppointmentHistoryResponseModel.nextAction == 15 ? AppColors.textColor : Colors.white,
fontSize: 16.f, fontSize: 16.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -1121,8 +1100,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false); myAppointmentsViewModel.getPatientAppointments(true, false);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isAutoDismiss: true, isCloseButtonVisible: true, isDismissible: false, isFullScreen: false, }, title: "", height: ResponsiveExtension.screenHeight * 0.3, isAutoDismiss: true, isCloseButtonVisible: true, isDismissible: false, isFullScreen: false, isSuccessDialog: true);
isSuccessDialog: true);
}); });
// LoaderBottomSheet.hideLoader(); // LoaderBottomSheet.hideLoader();
case 15: case 15:

@ -90,12 +90,14 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
.toString() .toString()
.toText16(isBold: true, maxlines: 1), .toText16(isBold: true, maxlines: 1),
SizedBox(width: 12.w), SizedBox(width: 12.w),
(bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL != null && bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL!.isNotEmpty )? Image.network( (bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL != null && bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL!.isNotEmpty)
bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", ? Image.network(
width: 20.h, bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png",
height: 15.h, width: 20.h,
fit: BoxFit.cover, height: 15.h,
) : SizedBox.shrink(), fit: BoxFit.cover,
)
: SizedBox.shrink(),
], ],
), ),
SizedBox(height: 2.h), SizedBox(height: 2.h),
@ -162,7 +164,9 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
runSpacing: 4.h, runSpacing: 4.h,
children: [ children: [
AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} ${LocaleKeys.yearsOld.tr(context: context)}"), AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} ${LocaleKeys.yearsOld.tr(context: context)}"),
AppCustomChipWidget(labelText: "${LocaleKeys.gender.tr(context: context)}: ${appState.getAuthenticatedUser()?.gender == 1 ? LocaleKeys.malE.tr(context: context) : LocaleKeys.femaleGender.tr(context: context)}"), AppCustomChipWidget(
labelText:
"${LocaleKeys.gender.tr(context: context)}: ${appState.getAuthenticatedUser()?.gender == 1 ? LocaleKeys.malE.tr(context: context) : LocaleKeys.femaleGender.tr(context: context)}"),
], ],
), ),
], ],
@ -191,19 +195,19 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
bookAppointmentsViewModel.appointmentNearestGateResponseModel != null bookAppointmentsViewModel.appointmentNearestGateResponseModel != null
? Wrap( ? Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
spacing: 8.w, spacing: 8.w,
runSpacing: 8.h, runSpacing: 8.h,
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
labelText: bookAppointmentsVM.isAppointmentNearestGateLoading labelText: bookAppointmentsVM.isAppointmentNearestGateLoading
? "Floor: Ground Floor" ? "Floor: Ground Floor"
: "${LocaleKeys.floor.tr(context: context)} ${getIt.get<AppState>().isArabic() ? bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocationN : bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocation}", : "${LocaleKeys.floor.tr(context: context)} ${getIt.get<AppState>().isArabic() ? bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocationN : bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocation}",
).toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), ).toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading),
AppCustomChipWidget( AppCustomChipWidget(
labelText: labelText:
"${LocaleKeys.nearestGate.tr(context: context)} ${getIt.get<AppState>().isArabic() ? bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumberN : bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumber}") "${LocaleKeys.nearestGate.tr(context: context)} ${getIt.get<AppState>().isArabic() ? bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumberN : bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumber}")
.toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), .toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading),
], ],
) )
: SizedBox.shrink(), : SizedBox.shrink(),
], ],
@ -329,23 +333,16 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async { await Future.delayed(Duration(milliseconds: 50)).then((value) async {
// LoaderBottomSheet.showLoader(loadingText: LocaleKeys.appointmentSuccess.tr()); // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.appointmentSuccess.tr());
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(context, child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()), callBackFunc: () {
context, bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false);
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()), bookAppointmentsViewModel.setIsLiveCareSchedule(false);
callBackFunc: () { Navigator.pushAndRemoveUntil(
bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false); context,
bookAppointmentsViewModel.setIsLiveCareSchedule(false); CustomPageRoute(
Navigator.pushAndRemoveUntil( page: LandingNavigation(),
context, ),
CustomPageRoute( (r) => false);
page: LandingNavigation(), }, isFullScreen: false, isCloseButtonVisible: false, isAutoDismiss: true);
),
(r) => false);
},
isFullScreen: false,
isCloseButtonVisible: false,
isAutoDismiss: true
);
}); });
}); });
} else { } else {
@ -368,23 +365,48 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
}, onSuccess: (apiResp) async { }, onSuccess: (apiResp) async {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async { await Future.delayed(Duration(milliseconds: 50)).then((value) async {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(context, child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h), callBackFunc: () async {
context, bookAppointmentsViewModel.setIsLiveCareSchedule(false);
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h), bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false);
callBackFunc: () { bool newValue = true;
bookAppointmentsViewModel.setIsLiveCareSchedule(false); await myAppointmentsViewModel.toggleReminder(
bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false); context: context,
Navigator.pushAndRemoveUntil( shouldCreateReminder: newValue,
context, patientAppointmentHistoryResponseModel: apiResp.patientAppointmentHistoryResponseModel,
CustomPageRoute( onSuccess: (result) {
page: LandingNavigation(), showCommonBottomSheet(
), context,
(r) => false); child: Utils.getSuccessWidget(
loadingText: newValue ? LocaleKeys.reminderSuccess.tr(context: context) : LocaleKeys.reminderCancelSuccess.tr(context: context),
),
callBackFunc: (str) {
Navigator.pushAndRemoveUntil(context, CustomPageRoute(page: LandingNavigation()), (r) => false);
},
title: "",
isCloseButtonVisible: false,
isDismissible: true,
isFullScreen: false,
isAutoDismiss: true,
height: ResponsiveExtension.screenHeight * 0.3,
);
},
onError: (error) {
setState(() {
apiResp.patientAppointmentHistoryResponseModel.hasReminder = !newValue;
});
showCommonBottomSheetWithoutHeight(
context,
title: LocaleKeys.notice.tr(context: context),
child: Utils.getErrorWidget(loadingText: error),
callBackFunc: () {
Navigator.pushAndRemoveUntil(context, CustomPageRoute(page: LandingNavigation()), (r) => false);
},
isFullScreen: false,
isCloseButtonVisible: true,
);
}, },
isFullScreen: false, );
isCloseButtonVisible: false, }, isFullScreen: false, isCloseButtonVisible: false, isAutoDismiss: true);
isAutoDismiss: true
);
}); });
}); });
} }

@ -6,6 +6,7 @@ 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/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/location_util.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';
@ -19,6 +20,7 @@ import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/hmg_s
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/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/features/weather/weather_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/blood_donation/blood_donation_page.dart'; import 'package:hmg_patient_app_new/presentation/blood_donation/blood_donation_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
@ -47,47 +49,46 @@ 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'; import '../emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart';
class ServicesPage extends StatelessWidget { class ServicesPage extends StatefulWidget {
bool showBackIcon; bool showBackIcon;
ServicesPage({super.key, this.showBackIcon = true}); ServicesPage({super.key, this.showBackIcon = true});
@override
State<ServicesPage> createState() => _ServicesPageState();
}
class _ServicesPageState extends State<ServicesPage> {
late BloodDonationViewModel bloodDonationViewModel; late BloodDonationViewModel bloodDonationViewModel;
late MedicalFileViewModel medicalFileViewModel; late MedicalFileViewModel medicalFileViewModel;
late WeatherMonitorViewModel weatherVM;
late final List<HmgServicesComponentModel> hmgServices = [ late final List<HmgServicesComponentModel> hmgServices = [
HmgServicesComponentModel(11, LocaleKeys.emergencyServices.tr(), "", AppAssets.emergency_services_icon, bgColor: AppColors.primaryRedColor, true, route: null, onTap: () async { HmgServicesComponentModel(11, LocaleKeys.emergencyServices.tr(), "", AppAssets.emergency_services_icon, bgColor: AppColors.primaryRedColor, true, route: null, onTap: () async {
// if (getIt.get<AppState>().isAuthenticated) { // if (getIt.get<AppState>().isAuthenticated) {
// getIt.get<EmergencyServicesViewModel>().flushData(); // getIt.get<EmergencyServicesViewModel>().flushData();
// getIt.get<EmergencyServicesViewModel>().getTransportationOrders( // getIt.get<EmergencyServicesViewModel>().getTransportationOrders(
// showLoader: false, // showLoader: false,
// ); // );
// getIt.get<EmergencyServicesViewModel>().getRRTOrders( // getIt.get<EmergencyServicesViewModel>().getRRTOrders(
// showLoader: false, // showLoader: false,
// ); // );
Navigator.of(getIt.get<NavigationService>().navigatorKey.currentContext!).push( Navigator.of(getIt.get<NavigationService>().navigatorKey.currentContext!).push(
CustomPageRoute( CustomPageRoute(
page: EmergencyServicesPage(), page: EmergencyServicesPage(),
settings: const RouteSettings(name: '/EmergencyServicesPage'), settings: const RouteSettings(name: '/EmergencyServicesPage'),
), ),
); );
// } else { // } else {
// await getIt.get<AuthenticationViewModel>().onLoginPressed(); // await getIt.get<AuthenticationViewModel>().onLoginPressed();
// } // }
}), }),
HmgServicesComponentModel( HmgServicesComponentModel(11, LocaleKeys.bookAppointment.tr(), "", AppAssets.appointment_calendar_icon, bgColor: AppColors.bookAppointment, true, route: null, onTap: () {
11, getIt.get<BookAppointmentsViewModel>().onTabChanged(0);
LocaleKeys.bookAppointment.tr(), Navigator.of(getIt<NavigationService>().navigatorKey.currentContext!).push(CustomPageRoute(page: BookAppointmentPage()));
"", }),
AppAssets.appointment_calendar_icon,
bgColor: AppColors.bookAppointment,
true,
route: null,
onTap: () {
getIt.get<BookAppointmentsViewModel>().onTabChanged(0);
Navigator.of(getIt<NavigationService>().navigatorKey.currentContext!).push(CustomPageRoute(page: BookAppointmentPage()));
}
),
HmgServicesComponentModel(5, LocaleKeys.completeCheckup.tr(), "", AppAssets.comprehensiveCheckup, bgColor: AppColors.bgGreenColor, true, route: null, onTap: () async { HmgServicesComponentModel(5, LocaleKeys.completeCheckup.tr(), "", AppAssets.comprehensiveCheckup, bgColor: AppColors.bgGreenColor, true, route: null, onTap: () async {
if (getIt.get<AppState>().isAuthenticated) { if (getIt.get<AppState>().isAuthenticated) {
getIt.get<NavigationService>().pushPageRoute(AppRoutes.comprehensiveCheckupPage); getIt.get<NavigationService>().pushPageRoute(AppRoutes.comprehensiveCheckupPage);
@ -164,33 +165,31 @@ class ServicesPage extends StatelessWidget {
} }
}), }),
HmgServicesComponentModel( HmgServicesComponentModel(
3, 3,
LocaleKeys.bloodDonation.tr(context: getIt.get<NavigationService>().navigatorKey.currentContext!), LocaleKeys.bloodDonation.tr(context: getIt.get<NavigationService>().navigatorKey.currentContext!),
"".needTranslation, "".needTranslation,
AppAssets.blood_donation_icon, AppAssets.blood_donation_icon,
bgColor: AppColors.bloodDonationCardColor, bgColor: AppColors.bloodDonationCardColor,
true, true,
route: null, route: null, onTap: () {
onTap: () { if (getIt.get<AppState>().isAuthenticated) {
if (getIt.get<AppState>().isAuthenticated) { Navigator.of(getIt<NavigationService>().navigatorKey.currentContext!).push(
Navigator.of(getIt<NavigationService>().navigatorKey.currentContext!).push( CustomPageRoute(
CustomPageRoute( page: BloodDonationPage(),
page: BloodDonationPage(), ),
), );
); } else {
} else { Navigator.of(getIt<NavigationService>().navigatorKey.currentContext!).push(
Navigator.of(getIt<NavigationService>().navigatorKey.currentContext!).push( CustomPageRoute(
CustomPageRoute( page: ServiceInfoPage(
page: ServiceInfoPage( serviceName: LocaleKeys.bloodDonation.tr(),
serviceName: LocaleKeys.bloodDonation.tr(), serviceHeader: LocaleKeys.bloodDonationServiceHeader.tr(),
serviceHeader: LocaleKeys.bloodDonationServiceHeader.tr(), serviceDescription: LocaleKeys.bloodDonationServiceDescription.tr(),
serviceDescription: LocaleKeys.bloodDonationServiceDescription.tr(), serviceImage: AppAssets.bloodDonationService),
serviceImage: AppAssets.bloodDonationService), ),
), );
);
}
} }
), }),
HmgServicesComponentModel( HmgServicesComponentModel(
113, 113,
LocaleKeys.servicePriceList.tr(context: getIt.get<NavigationService>().navigatorKey.currentContext!), LocaleKeys.servicePriceList.tr(context: getIt.get<NavigationService>().navigatorKey.currentContext!),
@ -375,21 +374,100 @@ class ServicesPage extends StatelessWidget {
), ),
]; ];
@override
void initState() {
// TODO: implement initState
super.initState();
weatherVM = getIt<WeatherMonitorViewModel>();
weatherVM.fetchCityInfoList();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bloodDonationViewModel = Provider.of<BloodDonationViewModel>(context); bloodDonationViewModel = Provider.of<BloodDonationViewModel>(context);
medicalFileViewModel = Provider.of<MedicalFileViewModel>(context); medicalFileViewModel = Provider.of<MedicalFileViewModel>(context);
hmgServices.removeWhere((element) => Utils.havePrivilege(element.action) == false); hmgServices.removeWhere((element) => Utils.havePrivilege(element.action) == false);
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView( body: CollapsingListView(
title: LocaleKeys.exploreServices.tr(), title: LocaleKeys.exploreServices.tr(),
isLeading: showBackIcon, isLeading: widget.showBackIcon,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row(
children: [
Flexible(
child: Container(
padding: EdgeInsets.all(16.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false),
child: Column(
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: "assets/images/svg/cloudyweather.svg", width: 64.w, height: 64.w).onPress(() {
// locationUtils?.getCurrentLocation();
print(getIt.get<AppState>().userLat);
print(getIt.get<AppState>().userLong);
}),
SizedBox(width: 12.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"August 19, 2025 - Tuesday, 11:25 ".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
SizedBox(
height: 4.w,
),
"Cloudy 38oC".toText16(isBold: true, weight: FontWeight.w600),
SizedBox(
height: 4.w,
),
"Health tips based on current weather".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500)
],
),
],
),
SizedBox(height: 16.w),
Container(
padding: EdgeInsets.all(16.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.lightGrayBGColor,
borderRadius: 10.r,
hasShadow: false,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: "assets/images/svg/morning.svg", width: 24.w, height: 24.h),
SizedBox(width: 4.w),
DateTime.now().toLocal().toString().substring(11, 16).toText14(isBold: true),
],
),
SizedBox(width: 25.w),
DateTime.now().toLocal().toString().substring(11, 16).toText14(isBold: true),
SizedBox(width: 25.w),
Row(
children: [
Utils.buildSvgWithAssets(icon: "assets/images/svg/evening.svg", width: 24.w, height: 24.h),
SizedBox(width: 4.w),
DateTime.now().toLocal().toString().substring(11, 16).toText14(isBold: true),
],
),
],
).expanded,
).paddingSymmetrical(0.w, 8.w)
],
),
),
)
],
).paddingSymmetrical(24.w, 0.w),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.medicalAndCareServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0), LocaleKeys.medicalAndCareServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -440,173 +518,176 @@ class ServicesPage extends StatelessWidget {
), ),
), ),
SizedBox(height: 24.h), SizedBox(height: 24.h),
getIt.get<AppState>().isAuthenticated ? Column( getIt.get<AppState>().isAuthenticated
crossAxisAlignment: CrossAxisAlignment.start, ? Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
LocaleKeys.personalServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0), children: [
SizedBox(height: 16.h), LocaleKeys.personalServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0),
Row( SizedBox(height: 16.h),
children: [ Row(
Expanded( children: [
child: Container( Expanded(
height: 183.h, child: Container(
width: 183.h, height: 183.h,
padding: EdgeInsets.all(16.w), width: 183.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( padding: EdgeInsets.all(16.w),
color: AppColors.whiteColor, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
borderRadius: 20.r, color: AppColors.whiteColor,
hasShadow: false, borderRadius: 20.r,
), hasShadow: false,
child: Column( ),
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Row( children: [
spacing: 8.w, Row(
crossAxisAlignment: CrossAxisAlignment.center, spacing: 8.w,
children: [ crossAxisAlignment: CrossAxisAlignment.center,
Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h, applyThemeColor: false), children: [
LocaleKeys.habibWallet.tr().toText14(isBold: true, maxlines: 2).expanded, Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h, applyThemeColor: false),
Utils.buildSvgWithAssets(icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward), LocaleKeys.habibWallet.tr().toText14(isBold: true, maxlines: 2).expanded,
], Utils.buildSvgWithAssets(icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward),
), ],
Spacer(), ),
getIt.get<AppState>().isAuthenticated Spacer(),
? Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) { getIt.get<AppState>().isAuthenticated
return Utils.getPaymentAmountWithSymbol2(num.parse(NumberFormat.decimalPattern().format(habibWalletVM.habibWalletAmount)), isExpanded: false, letterSpacing: -1) ? Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
.toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h); return Utils.getPaymentAmountWithSymbol2(num.parse(NumberFormat.decimalPattern().format(habibWalletVM.habibWalletAmount)),
}) isExpanded: false, letterSpacing: -1)
: LocaleKeys.loginToViewWalletBalance.tr().toText12(isBold: true, maxLine: 2), .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h);
Spacer(), })
getIt.get<AppState>().isAuthenticated : LocaleKeys.loginToViewWalletBalance.tr().toText12(isBold: true, maxLine: 2),
? CustomButton( Spacer(),
height: 40.h, getIt.get<AppState>().isAuthenticated
icon: AppAssets.recharge_icon, ? CustomButton(
iconSize: 24.w, height: 40.h,
iconColor: AppColors.infoColor, icon: AppAssets.recharge_icon,
textColor: AppColors.infoColor, iconSize: 24.w,
text: LocaleKeys.recharge.tr(), iconColor: AppColors.infoColor,
borderWidth: 0.w, textColor: AppColors.infoColor,
isBold: true, text: LocaleKeys.recharge.tr(),
borderColor: Colors.transparent, borderWidth: 0.w,
backgroundColor: Color(0xff45A2F8).withValues(alpha: 0.08), isBold: true,
padding: EdgeInsets.all(8.w), borderColor: Colors.transparent,
fontSize: 14.f, backgroundColor: Color(0xff45A2F8).withValues(alpha: 0.08),
onPressed: () { padding: EdgeInsets.all(8.w),
Navigator.of(context).push(CustomPageRoute(page: RechargeWalletPage())); fontSize: 14.f,
}, onPressed: () {
) Navigator.of(context).push(CustomPageRoute(page: RechargeWalletPage()));
: SizedBox.shrink(), },
], )
).onPress(() async { : SizedBox.shrink(),
if (getIt.get<AppState>().isAuthenticated) { ],
Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage())); ).onPress(() async {
} else { if (getIt.get<AppState>().isAuthenticated) {
await getIt.get<AuthenticationViewModel>().onLoginPressed(); Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage()));
} } else {
}), await getIt.get<AuthenticationViewModel>().onLoginPressed();
), }
), }),
SizedBox(width: 16.w),
Expanded(
child: Container(
height: 183.h,
width: 183.h,
padding: EdgeInsets.all(16.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.r,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
spacing: 8.w,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.services_medical_file_icon, width: 40.w, height: 40.h, applyThemeColor: false),
LocaleKeys.medicalFile.tr().toText16(isBold: true, maxlines: 2).expanded,
Utils.buildSvgWithAssets(icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward),
],
), ),
Spacer(), ),
getIt.get<AppState>().isAuthenticated SizedBox(width: 16.w),
? Wrap( Expanded(
spacing: -12.h, child: Container(
// runSpacing: 0.h, height: 183.h,
children: [ width: 183.h,
Utils.buildImgWithAssets( padding: EdgeInsets.all(16.w),
icon: AppAssets.babyGirlImg, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
height: 32.h, color: AppColors.whiteColor,
width: 32.w, borderRadius: 20.r,
border: 1, hasShadow: false,
fit: BoxFit.contain,
borderRadius: 50.r,
),
Utils.buildImgWithAssets(
icon: AppAssets.femaleImg,
height: 32.h,
width: 32.w,
border: 1,
borderRadius: 50.r,
fit: BoxFit.contain,
),
Utils.buildImgWithAssets(
icon: AppAssets.maleImg,
height: 32.h,
width: 32.w,
border: 1,
borderRadius: 50.r,
fit: BoxFit.contain,
),
],
)
: LocaleKeys.loginToViewMedicalFile.tr().toText12(isBold: true, maxLine: 2),
Spacer(),
getIt.get<AppState>().isAuthenticated
? CustomButton(
height: 40.h,
icon: AppAssets.add_icon,
iconSize: 24.h,
iconColor: AppColors.primaryRedColor,
textColor: AppColors.primaryRedColor,
text: LocaleKeys.addMember.tr(),
borderWidth: 0.w,
isBold: true,
borderColor: Colors.transparent,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.08),
padding: EdgeInsets.all(8.w),
fontSize: 14.f,
onPressed: () {
DialogService dialogService = getIt.get<DialogService>();
medicalFileViewModel.clearAuthValues();
dialogService.showAddFamilyFileSheet(
label: LocaleKeys.addFamilyMember.tr(),
message: LocaleKeys.pleaseFillBelowFieldToAddNewFamilyMember.tr(),
onVerificationPress: () {
medicalFileViewModel.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms);
});
},
)
: SizedBox.shrink(),
],
).onPress(() async {
if (getIt.get<AppState>().isAuthenticated) {
Navigator.of(context).push(
CustomPageRoute(
page: MedicalFilePage(),
), ),
); child: Column(
} else { crossAxisAlignment: CrossAxisAlignment.start,
await getIt.get<AuthenticationViewModel>().onLoginPressed(); children: [
} Row(
}), spacing: 8.w,
), crossAxisAlignment: CrossAxisAlignment.center,
), children: [
], Utils.buildSvgWithAssets(icon: AppAssets.services_medical_file_icon, width: 40.w, height: 40.h, applyThemeColor: false),
).paddingSymmetrical(24.w, 0), LocaleKeys.medicalFile.tr().toText16(isBold: true, maxlines: 2).expanded,
], Utils.buildSvgWithAssets(icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward),
) : SizedBox(), ],
),
Spacer(),
getIt.get<AppState>().isAuthenticated
? Wrap(
spacing: -12.h,
// runSpacing: 0.h,
children: [
Utils.buildImgWithAssets(
icon: AppAssets.babyGirlImg,
height: 32.h,
width: 32.w,
border: 1,
fit: BoxFit.contain,
borderRadius: 50.r,
),
Utils.buildImgWithAssets(
icon: AppAssets.femaleImg,
height: 32.h,
width: 32.w,
border: 1,
borderRadius: 50.r,
fit: BoxFit.contain,
),
Utils.buildImgWithAssets(
icon: AppAssets.maleImg,
height: 32.h,
width: 32.w,
border: 1,
borderRadius: 50.r,
fit: BoxFit.contain,
),
],
)
: LocaleKeys.loginToViewMedicalFile.tr().toText12(isBold: true, maxLine: 2),
Spacer(),
getIt.get<AppState>().isAuthenticated
? CustomButton(
height: 40.h,
icon: AppAssets.add_icon,
iconSize: 24.h,
iconColor: AppColors.primaryRedColor,
textColor: AppColors.primaryRedColor,
text: LocaleKeys.addMember.tr(),
borderWidth: 0.w,
isBold: true,
borderColor: Colors.transparent,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.08),
padding: EdgeInsets.all(8.w),
fontSize: 14.f,
onPressed: () {
DialogService dialogService = getIt.get<DialogService>();
medicalFileViewModel.clearAuthValues();
dialogService.showAddFamilyFileSheet(
label: LocaleKeys.addFamilyMember.tr(),
message: LocaleKeys.pleaseFillBelowFieldToAddNewFamilyMember.tr(),
onVerificationPress: () {
medicalFileViewModel.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms);
});
},
)
: SizedBox.shrink(),
],
).onPress(() async {
if (getIt.get<AppState>().isAuthenticated) {
Navigator.of(context).push(
CustomPageRoute(
page: MedicalFilePage(),
),
);
} else {
await getIt.get<AuthenticationViewModel>().onLoginPressed();
}
}),
),
),
],
).paddingSymmetrical(24.w, 0),
],
)
: SizedBox(),
SizedBox(height: 24.h), SizedBox(height: 24.h),
LocaleKeys.healthTools.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0), LocaleKeys.healthTools.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0),
SizedBox(height: 16.h), SizedBox(height: 16.h),

Loading…
Cancel
Save