diff --git a/assets/images/svg/blood_pressure_icon.svg b/assets/images/svg/blood_pressure_icon.svg
new file mode 100644
index 0000000..0b027ad
--- /dev/null
+++ b/assets/images/svg/blood_pressure_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/blood_sugar_only_icon.svg b/assets/images/svg/blood_sugar_only_icon.svg
new file mode 100644
index 0000000..f81cee8
--- /dev/null
+++ b/assets/images/svg/blood_sugar_only_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/low_indicator_icon.svg b/assets/images/svg/low_indicator_icon.svg
new file mode 100644
index 0000000..f2ca09f
--- /dev/null
+++ b/assets/images/svg/low_indicator_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/normal_status_green_icon.svg b/assets/images/svg/normal_status_green_icon.svg
new file mode 100644
index 0000000..b3f2619
--- /dev/null
+++ b/assets/images/svg/normal_status_green_icon.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/images/svg/send_email_icon.svg b/assets/images/svg/send_email_icon.svg
new file mode 100644
index 0000000..eb8684a
--- /dev/null
+++ b/assets/images/svg/send_email_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/weight_icon.svg b/assets/images/svg/weight_icon.svg
new file mode 100644
index 0000000..f93c662
--- /dev/null
+++ b/assets/images/svg/weight_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart
index f366329..039787b 100644
--- a/lib/core/api/api_client.dart
+++ b/lib/core/api/api_client.dart
@@ -210,7 +210,7 @@ class ApiClientImp implements ApiClient {
final int statusCode = response.statusCode;
log("uri: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
- // log("response.body: ${response.body}");
+ log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) {
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));
logApiEndpointError(endPoint, 'Error While Fetching data', statusCode);
diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart
index 65ac1ef..246a801 100644
--- a/lib/core/api_consts.dart
+++ b/lib/core/api_consts.dart
@@ -398,19 +398,6 @@ 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';
-///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';
@@ -420,7 +407,6 @@ var GET_QUESTION_TYPES = 'Services/OUTPs.svc/REST/getQuestionsTypes';
var UPDATE_DIABETIC_RESULT = 'Services/Patients.svc/REST/Patient_UpdateDiabeticResult';
-var SEND_AVERAGE_BLOOD_SUGAR_REPORT = 'Services/Notifications.svc/REST/SendAverageBloodSugarReport';
var DEACTIVATE_DIABETIC_STATUS = 'services/Patients.svc/REST/Patient_DeactivateDiabeticStatus';
var DEACTIVATE_BLOOD_PRESSURES_STATUS = 'services/Patients.svc/REST/Patient_DeactivateBloodPressureStatus';
@@ -826,7 +812,6 @@ class ApiConsts {
static final String addHHCOrder = 'api/HHC/add';
// SYMPTOMS CHECKER API
-
static final String symptomsUserLogin = '$symptomsCheckerApi/user_login';
static final String getBodySymptomsByName = '$symptomsCheckerApi/GetBodySymptomsByName';
static final String getRiskFactors = '$symptomsCheckerApi/GetRiskFactors';
@@ -850,6 +835,31 @@ class ApiConsts {
static String h2oUpdateUserDetail = "Services/H2ORemainder.svc/REST/H2O_UpdateUserDetails_New";
static String h2oUndoUserActivity = "Services/H2ORemainder.svc/REST/H2o_UndoUserActivity";
+ // HEALTH TRACKERS
+ // Blood Sugar (Diabetic)
+ static String getDiabeticResultAverage = 'Services/Patients.svc/REST/Patient_GetDiabeticResultAverage';
+ static String getDiabeticResult = 'Services/Patients.svc/REST/Patient_GetDiabtecResults';
+ static String addDiabeticResult = 'Services/Patients.svc/REST/Patient_AddDiabtecResult';
+ static String updateDiabeticResult = 'Services/Patients.svc/REST/Patient_UpdateDiabtecResult';
+ static String deactivateDiabeticStatus = 'Services/Patients.svc/REST/Patient_DeactivateDiabeticStatus';
+ static String sendAverageBloodSugarReport = 'Services/Notifications.svc/REST/SendAverageBloodSugarReport';
+
+ // Blood Pressure
+ static String getBloodPressureResultAverage = 'Services/Patients.svc/REST/Patient_GetBloodPressureResultAverage';
+ static String getBloodPressureResult = 'Services/Patients.svc/REST/Patient_GetBloodPressureResult';
+ static String addBloodPressureResult = 'Services/Patients.svc/REST/Patient_AddBloodPressureResult';
+ static String updateBloodPressureResult = 'Services/Patients.svc/REST/Patient_UpdateBloodPressureResult';
+ static String deactivateBloodPressureStatus = 'Services/Patients.svc/REST/Patient_DeactivateBloodPressureStatus';
+ static String sendAverageBloodPressureReport = 'Services/Notifications.svc/REST/SendAverageBloodPressureReport';
+
+ // Weight Measurement
+ static String getWeightMeasurementResultAverage = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResultAverage';
+ static String getWeightMeasurementResult = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResult';
+ static String addWeightMeasurementResult = 'Services/Patients.svc/REST/Patient_AddWeightMeasurementResult';
+ static String updateWeightMeasurementResult = 'Services/Patients.svc/REST/Patient_UpdateWeightMeasurementResult';
+ static String deactivateWeightMeasurementStatus = 'Services/Patients.svc/REST/Patient_DeactivateWeightMeasurementStatus';
+ static String sendAverageBodyWeightReport = 'Services/Notifications.svc/REST/SendAverageBodyWeightReport';
+
// ************ static values for Api ****************
static final double appVersionID = 50.3;
static final int appChannelId = 3;
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index 2fdc389..741cb6b 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -267,16 +267,21 @@ class AppAssets {
static const String yellowArrowDownIcon = '$svgBasePath/yellow_arrow_down_icon.svg';
static const String greenTickIcon = '$svgBasePath/green_tick_icon.svg';
- // PNGS
-
-
static const String bloodSugar = '$svgBasePath/bloodsugar.svg';
static const String bloodCholestrol = '$svgBasePath/bloodcholestrol.svg';
static const String triglycerides = '$svgBasePath/triglycerides.svg';
static const String bulb = '$svgBasePath/bulb.svg';
static const String switchBtn = '$svgBasePath/switch.svg';
+ //Health Trackers
+ static const String bloodPressureIcon = '$svgBasePath/blood_pressure_icon.svg';
+ static const String bloodSugarOnlyIcon = '$svgBasePath/blood_sugar_only_icon.svg';
+ static const String weightIcon = '$svgBasePath/weight_icon.svg';
+ static const String normalStatusGreenIcon = '$svgBasePath/normal_status_green_icon.svg';
+ static const String sendEmailIcon = '$svgBasePath/send_email_icon.svg';
+ static const String lowIndicatorIcon = '$svgBasePath/low_indicator_icon.svg';
+ // Health Calculators
static const String ovulationAccordion = '$svgBasePath/approximate_ovulation_accordion.svg';
static const String nextPeriodAccordion = '$svgBasePath/next_period_accordion.svg';
static const String fertileAccordion = '$svgBasePath/fertile_window_accordion.svg';
@@ -285,9 +290,7 @@ class AppAssets {
static const String covid19icon = '$svgBasePath/covid_19.svg';
-
//vital sign
-
static const String heartRate = '$svgBasePath/heart_rate.svg';
static const String respRate = '$svgBasePath/resp_rate.svg';
static const String weightVital = '$svgBasePath/weight_2.svg';
@@ -296,8 +299,6 @@ class AppAssets {
static const String bloodPressure = '$svgBasePath/blood_pressure.svg';
static const String temperature = '$svgBasePath/temperature.svg';
-
-
// PNGS //
static const String hmgLogo = '$pngBasePath/hmg_logo.png';
static const String liveCareService = '$pngBasePath/livecare_service.png';
@@ -324,8 +325,6 @@ class AppAssets {
static const String fullBodyFront = '$pngBasePath/full_body_front.png';
static const String fullBodyBack = '$pngBasePath/full_body_back.png';
static const String bmiFullBody = '$pngBasePath/bmi_image_1.png';
-
-
}
class AppAnimations {
@@ -346,6 +345,4 @@ class AppAnimations {
static const String ambulance = '$lottieBasePath/ambulance.json';
static const String ambulanceAlert = '$lottieBasePath/ambulance_alert.json';
static const String rrtAmbulance = '$lottieBasePath/rrt_ambulance.json';
-
-
}
diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart
index c6c5554..f284b39 100644
--- a/lib/core/dependencies.dart
+++ b/lib/core/dependencies.dart
@@ -18,6 +18,7 @@ import 'package:hmg_patient_app_new/features/emergency_services/emergency_servic
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_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart';
@@ -50,6 +51,7 @@ 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/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/presentation/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/cache_service.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
@@ -141,6 +143,7 @@ class AppDependencies {
getIt.registerLazySingleton(() => BloodDonationRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => WaterMonitorRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => MyInvoicesRepoImp(loggerService: getIt(), apiClient: getIt()));
+ getIt.registerLazySingleton(() => HealthTrackersRepoImp(loggerService: getIt(), apiClient: getIt()));
// ViewModels
// Global/shared VMs → LazySingleton
@@ -167,10 +170,7 @@ class AppDependencies {
);
getIt.registerLazySingleton(
- () => HabibWalletViewModel(
- habibWalletRepo: getIt(),
- errorHandlerService: getIt()
- ),
+ () => HabibWalletViewModel(habibWalletRepo: getIt(), errorHandlerService: getIt()),
);
getIt.registerLazySingleton(
@@ -273,8 +273,13 @@ class AppDependencies {
getIt.registerLazySingleton(() => HealthProvider());
- getIt.registerLazySingleton(() => WaterMonitorViewModel(waterMonitorRepo: getIt()));
+ getIt.registerLazySingleton(() => WaterMonitorViewModel(waterMonitorRepo: getIt(), errorHandlerService: getIt()));
- getIt.registerLazySingleton(() => MyInvoicesViewModel(myInvoicesRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
+ getIt.registerLazySingleton(() => MyInvoicesViewModel(
+ myInvoicesRepo: getIt(),
+ errorHandlerService: getIt(),
+ navServices: getIt(),
+ ));
+ getIt.registerLazySingleton(() => HealthTrackersViewModel(healthTrackersRepo: getIt(), errorHandlerService: getIt()));
}
}
diff --git a/lib/core/enums.dart b/lib/core/enums.dart
index 8fd4818..6dc3bf6 100644
--- a/lib/core/enums.dart
+++ b/lib/core/enums.dart
@@ -16,7 +16,7 @@ enum CountryEnum { saudiArabia, unitedArabEmirates }
enum CalenderEnum { gregorian, hijri }
-enum SelectionTypeEnum { dropdown, calendar, search }
+enum SelectionTypeEnum { dropdown, calendar, search, time }
enum GenderTypeEnum { male, female }
@@ -38,7 +38,19 @@ enum HealthCalConEnum { calculator, converter }
enum HealthCalculatorEnum { general, women }
-enum HealthCalculatorsTypeEnum { bmi, calories, bmr, idealBodyWeight, bodyFat, crabsProteinFat, ovulation, deliveryDueDate, bloodSugar, bloodCholesterol, triglycerides }
+enum HealthCalculatorsTypeEnum {
+ bmi,
+ calories,
+ bmr,
+ idealBodyWeight,
+ bodyFat,
+ crabsProteinFat,
+ ovulation,
+ deliveryDueDate,
+ bloodSugar,
+ bloodCholesterol,
+ triglycerides
+}
extension HealthCalculatorExtenshion on HealthCalculatorsTypeEnum {
String get displayName {
@@ -297,3 +309,5 @@ extension ServiceTypeEnumExt on ServiceTypeEnum {
// SymptomsChecker
enum PossibleConditionsSeverityEnum { seekMedicalAdvice, monitorOnly, emergency }
+
+enum HealthTrackerTypeEnum { bloodSugar, bloodPressure, weightTracker }
diff --git a/lib/core/utils/size_utils.dart b/lib/core/utils/size_utils.dart
index 4fdc09c..02b8195 100644
--- a/lib/core/utils/size_utils.dart
+++ b/lib/core/utils/size_utils.dart
@@ -7,16 +7,14 @@ import 'package:flutter/material.dart'; // These are the Viewport values of your
const num figmaDesignWidth = 375; // iPhone X / 12 base width
const num figmaDesignHeight = 812; // iPhone X / 12 base height
-
extension ConstrainedResponsive on num {
/// Width with max cap for tablets
- double get wCapped => isTablet ? math.min( w, this * 1.3) : w;
+ double get wCapped => isTablet ? math.min(w, this * 1.3) : w;
/// Height with max cap for tablets
double get hCapped => isTablet ? math.min(h, this * 1.3) : h;
}
-
extension ResponsiveExtension on num {
double get _screenWidth => SizeUtils.width;
diff --git a/lib/features/health_trackers/health_trackers_repo.dart b/lib/features/health_trackers/health_trackers_repo.dart
new file mode 100644
index 0000000..e6930f9
--- /dev/null
+++ b/lib/features/health_trackers/health_trackers_repo.dart
@@ -0,0 +1,752 @@
+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/services/logger_service.dart';
+
+/// Progress types to request different ranges from the progress API.
+enum ProgressType { today, week, month }
+
+abstract class HealthTrackersRepo {
+ // ==================== BLOOD SUGAR (DIABETIC) ====================
+ /// Get blood sugar result averages (week, month, year).
+ Future>> getDiabeticResultAverage();
+
+ /// Get blood sugar results (week, month, year).
+ Future>> getDiabeticResults();
+
+ /// Add new blood sugar result.
+ Future>> addDiabeticResult({
+ required String bloodSugarDateChart,
+ required String bloodSugarResult,
+ required String diabeticUnit,
+ required int measuredTime,
+ });
+
+ /// Update existing blood sugar result.
+ Future>> updateDiabeticResult({
+ required DateTime month,
+ required DateTime hour,
+ required String bloodSugarResult,
+ required String diabeticUnit,
+ required int measuredTime,
+ required int lineItemNo,
+ });
+
+ /// Deactivate blood sugar record.
+ Future>> deactivateDiabeticStatus({
+ required int lineItemNo,
+ });
+
+ // ==================== BLOOD PRESSURE ====================
+ /// Get blood pressure result averages (week, month, year).
+ Future>> getBloodPressureResultAverage();
+
+ /// Get blood pressure results (week, month, year).
+ Future>> getBloodPressureResults();
+
+ /// Add new blood pressure result.
+ Future>> addBloodPressureResult({
+ required String bloodPressureDate,
+ required String diastolicPressure,
+ required String systolicePressure,
+ required int measuredArm,
+ });
+
+ /// Update existing blood pressure result.
+ Future>> updateBloodPressureResult({
+ required String bloodPressureDate,
+ required String diastolicPressure,
+ required String systolicePressure,
+ required int measuredArm,
+ required int lineItemNo,
+ });
+
+ /// Deactivate blood pressure record.
+ Future>> deactivateBloodPressureStatus({
+ required int lineItemNo,
+ });
+
+ // ==================== WEIGHT MEASUREMENT ====================
+ /// Get weight measurement result averages (week, month, year).
+ Future>> getWeightMeasurementResultAverage();
+
+ /// Get weight measurement results (week, month, year).
+ Future>> getWeightMeasurementResults();
+
+ /// Add new weight measurement result.
+ Future>> addWeightMeasurementResult({
+ required String weightDate,
+ required String weightMeasured,
+ required int weightUnit,
+ });
+
+ /// Update existing weight measurement result.
+ Future>> updateWeightMeasurementResult({
+ required int lineItemNo,
+ required int weightUnit,
+ required String weightMeasured,
+ required String weightDate,
+ });
+
+ /// Deactivate weight measurement record.
+ Future>> deactivateWeightMeasurementStatus({
+ required int lineItemNo,
+ });
+}
+
+class HealthTrackersRepoImp implements HealthTrackersRepo {
+ final ApiClient apiClient;
+ final LoggerService loggerService;
+
+ HealthTrackersRepoImp({required this.loggerService, required this.apiClient});
+
+ // ==================== BLOOD SUGAR (DIABETIC) METHODS ====================
+
+ @override
+ Future>> getDiabeticResultAverage() async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ ApiConsts.getDiabeticResultAverage,
+ body: {},
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ dynamic extracted;
+ if (response is Map) {
+ // Extract average lists
+ extracted = {
+ 'monthAverageList': response['List_MonthDiabtectResultAverage'] ?? [],
+ 'weekAverageList': response['List_WeekDiabtectResultAverage'] ?? [],
+ 'yearAverageList': response['List_YearDiabtecResultAverage'] ?? [],
+ };
+ } else {
+ extracted = response;
+ }
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: extracted,
+ );
+ } 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()));
+ }
+ }
+
+ @override
+ Future>> getDiabeticResults() async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ ApiConsts.getDiabeticResult,
+ body: {},
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ dynamic extracted;
+ if (response is Map) {
+ // Extract patient result lists
+ extracted = {
+ 'monthResultList': response['List_MonthDiabtecPatientResult'] ?? [],
+ 'weekResultList': response['List_WeekDiabtecPatientResult'] ?? [],
+ 'yearResultList': response['List_YearDiabtecPatientResult'] ?? [],
+ };
+ } else {
+ extracted = response;
+ }
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: extracted,
+ );
+ } 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()));
+ }
+ }
+
+ @override
+ Future>> addDiabeticResult({
+ required String bloodSugarDateChart,
+ required String bloodSugarResult,
+ required String diabeticUnit,
+ required int measuredTime,
+ }) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ Map body = {
+ 'BloodSugerDateChart': bloodSugarDateChart,
+ 'BloodSugerResult': bloodSugarResult,
+ 'DiabtecUnit': diabeticUnit,
+ 'MeasuredTime': measuredTime + 1, // Add 1 as per old service
+ };
+
+ await apiClient.post(
+ ApiConsts.addDiabeticResult,
+ body: body,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: response,
+ );
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> updateDiabeticResult({
+ required DateTime month,
+ required DateTime hour,
+ required String bloodSugarResult,
+ required String diabeticUnit,
+ required int measuredTime,
+ required int lineItemNo,
+ }) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ // Format: 'YYYY-MM-DD HH:MM:SS' as per old service
+ String formattedDate = '${month.year}-${month.month}-${month.day} ${hour.hour}:${hour.minute}:00';
+
+ Map body = {
+ 'BloodSugerDateChart': formattedDate,
+ 'BloodSugerResult': bloodSugarResult,
+ 'DiabtecUnit': diabeticUnit,
+ 'MeasuredTime': measuredTime + 1, // Add 1 as per old service
+ 'LineItemNo': lineItemNo,
+ };
+
+ await apiClient.post(
+ ApiConsts.updateDiabeticResult,
+ body: body,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: response,
+ );
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> deactivateDiabeticStatus({
+ required int lineItemNo,
+ }) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ Map body = {
+ 'LineItemNo': lineItemNo,
+ };
+
+ await apiClient.post(
+ ApiConsts.deactivateDiabeticStatus,
+ body: body,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: response,
+ );
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ // ==================== BLOOD PRESSURE METHODS ====================
+
+ @override
+ Future>> getBloodPressureResultAverage() async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ ApiConsts.getBloodPressureResultAverage,
+ body: {},
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ dynamic extracted;
+ if (response is Map) {
+ // Extract all three list types
+ extracted = {
+ 'monthList': response['List_MonthBloodPressureResultAverage'] ?? [],
+ 'weekList': response['List_WeekBloodPressureResultAverage'] ?? [],
+ 'yearList': response['List_YearBloodPressureResultAverage'] ?? [],
+ };
+ } else {
+ extracted = response;
+ }
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: extracted,
+ );
+ } 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()));
+ }
+ }
+
+ @override
+ Future>> getBloodPressureResults() async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ ApiConsts.getBloodPressureResult,
+ body: {},
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ dynamic extracted;
+ if (response is Map) {
+ // Extract all three list types
+ extracted = {
+ 'weekList': response['List_WeekBloodPressureResult'] ?? [],
+ 'monthList': response['List_MonthBloodPressureResult'] ?? [],
+ 'yearList': response['List_YearBloodPressureResult'] ?? [],
+ };
+ } else {
+ extracted = response;
+ }
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: extracted,
+ );
+ } 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()));
+ }
+ }
+
+ @override
+ Future>> addBloodPressureResult({
+ required String bloodPressureDate,
+ required String diastolicPressure,
+ required String systolicePressure,
+ required int measuredArm,
+ }) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ Map body = {
+ 'BloodPressureDate': bloodPressureDate,
+ 'DiastolicPressure': diastolicPressure,
+ 'SystolicePressure': systolicePressure,
+ 'MeasuredArm': measuredArm,
+ };
+
+ await apiClient.post(
+ ApiConsts.addBloodPressureResult,
+ body: body,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: response,
+ );
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> updateBloodPressureResult({
+ required String bloodPressureDate,
+ required String diastolicPressure,
+ required String systolicePressure,
+ required int measuredArm,
+ required int lineItemNo,
+ }) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ Map body = {
+ 'BloodPressureDate': bloodPressureDate,
+ 'DiastolicPressure': diastolicPressure,
+ 'SystolicePressure': systolicePressure,
+ 'MeasuredArm': measuredArm,
+ 'LineItemNo': lineItemNo,
+ };
+
+ await apiClient.post(
+ ApiConsts.updateBloodPressureResult,
+ body: body,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: response,
+ );
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> deactivateBloodPressureStatus({
+ required int lineItemNo,
+ }) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ Map body = {
+ 'LineItemNo': lineItemNo,
+ };
+
+ await apiClient.post(
+ ApiConsts.deactivateBloodPressureStatus,
+ body: body,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: response,
+ );
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ // ==================== WEIGHT MEASUREMENT METHODS ====================
+
+ @override
+ Future>> getWeightMeasurementResultAverage() async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ ApiConsts.getWeightMeasurementResultAverage,
+ body: {},
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ dynamic extracted;
+ if (response is Map) {
+ // Extract average lists
+ extracted = {
+ 'monthAverageList': response['List_MonthWeightMeasurementResultAverage'] ?? [],
+ 'weekAverageList': response['List_WeekWeightMeasurementResultAverage'] ?? [],
+ 'yearAverageList': response['List_YearWeightMeasurementResultAverage'] ?? [],
+ };
+ } else {
+ extracted = response;
+ }
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: extracted,
+ );
+ } 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()));
+ }
+ }
+
+ @override
+ Future>> getWeightMeasurementResults() async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ await apiClient.post(
+ ApiConsts.getWeightMeasurementResult,
+ body: {},
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ dynamic extracted;
+ if (response is Map) {
+ // Extract result lists
+ extracted = {
+ 'weekResultList': response['List_WeekWeightMeasurementResult'] ?? [],
+ 'monthResultList': response['List_MonthWeightMeasurementResult'] ?? [],
+ 'yearResultList': response['List_YearWeightMeasurementResult'] ?? [],
+ };
+ } else {
+ extracted = response;
+ }
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: extracted,
+ );
+ } 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()));
+ }
+ }
+
+ @override
+ Future>> addWeightMeasurementResult({
+ required String weightDate,
+ required String weightMeasured,
+ required int weightUnit,
+ }) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ Map body = {
+ 'WeightDate': weightDate,
+ 'WeightMeasured': weightMeasured,
+ 'weightUnit': weightUnit,
+ };
+
+ await apiClient.post(
+ ApiConsts.addWeightMeasurementResult,
+ body: body,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: response,
+ );
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> updateWeightMeasurementResult({
+ required int lineItemNo,
+ required int weightUnit,
+ required String weightMeasured,
+ required String weightDate,
+ }) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ Map body = {
+ 'LineItemNo': lineItemNo,
+ 'weightUnit': '$weightUnit', // Convert to string as per old service
+ 'WeightMeasured': weightMeasured,
+ 'WeightDate': weightDate,
+ };
+
+ await apiClient.post(
+ ApiConsts.updateWeightMeasurementResult,
+ body: body,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: response,
+ );
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> deactivateWeightMeasurementStatus({
+ required int lineItemNo,
+ }) async {
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+
+ Map body = {
+ 'LineItemNo': lineItemNo,
+ };
+
+ await apiClient.post(
+ ApiConsts.deactivateWeightMeasurementStatus,
+ body: body,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: errorMessage,
+ data: response,
+ );
+ },
+ );
+
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+}
diff --git a/lib/features/health_trackers/models/blood_pressure/blood_pressure_result.dart b/lib/features/health_trackers/models/blood_pressure/blood_pressure_result.dart
new file mode 100644
index 0000000..f024d8e
--- /dev/null
+++ b/lib/features/health_trackers/models/blood_pressure/blood_pressure_result.dart
@@ -0,0 +1,87 @@
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+
+class BloodPressureResult {
+ int? patientID;
+ int? lineItemNo;
+ DateTime? bloodPressureDate;
+ int? measuredArm;
+ int? systolicePressure;
+ int? diastolicPressure;
+ dynamic remark;
+ bool? isActive;
+ int? chartYear;
+ String? chartMonth;
+ dynamic yearSystolicePressureAverageResult;
+ dynamic monthSystolicePressureResult;
+ dynamic weekSystolicePressureResult;
+ int? yearDiastolicPressureAverageResult;
+ dynamic monthDiastolicPressureResult;
+ dynamic weekDiastolicPressureResult;
+ String? measuredArmDesc;
+ dynamic weekDesc;
+
+ BloodPressureResult(
+ {this.patientID,
+ this.lineItemNo,
+ this.bloodPressureDate,
+ this.measuredArm,
+ this.systolicePressure,
+ this.diastolicPressure,
+ this.remark,
+ this.isActive,
+ this.chartYear,
+ this.chartMonth,
+ this.yearSystolicePressureAverageResult,
+ this.monthSystolicePressureResult,
+ this.weekSystolicePressureResult,
+ this.yearDiastolicPressureAverageResult,
+ this.monthDiastolicPressureResult,
+ this.weekDiastolicPressureResult,
+ this.measuredArmDesc,
+ this.weekDesc});
+
+ BloodPressureResult.fromJson(Map json) {
+ patientID = json['PatientID'];
+ lineItemNo = json['LineItemNo'];
+ bloodPressureDate = DateUtil.convertStringToDate(json['BloodPressureDate']);
+ measuredArm = json['MeasuredArm'];
+ systolicePressure = json['SystolicePressure'];
+ diastolicPressure = json['DiastolicPressure'];
+ remark = json['Remark'];
+ isActive = json['IsActive'];
+ chartYear = json['ChartYear'];
+ chartMonth = json['ChartMonth'];
+ yearSystolicePressureAverageResult = json['YearSystolicePressureAverageResult'];
+ monthSystolicePressureResult = json['MonthSystolicePressureResult'];
+ weekSystolicePressureResult = json['WeekSystolicePressureResult'];
+ yearDiastolicPressureAverageResult = json['YearDiastolicPressureAverageResult'];
+ monthDiastolicPressureResult = json['MonthDiastolicPressureResult'];
+ weekDiastolicPressureResult = json['WeekDiastolicPressureResult'];
+ measuredArmDesc = json['MeasuredArmDesc'];
+ weekDesc = json['WeekDesc'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['PatientID'] = patientID;
+ data['LineItemNo'] = lineItemNo;
+ data['BloodPressureDate'] = bloodPressureDate;
+ data['MeasuredArm'] = measuredArm;
+ data['SystolicePressure'] = systolicePressure;
+ data['DiastolicPressure'] = diastolicPressure;
+ data['Remark'] = remark;
+ data['IsActive'] = isActive;
+ data['ChartYear'] = chartYear;
+ data['ChartMonth'] = chartMonth;
+ data['YearSystolicePressureAverageResult'] = yearSystolicePressureAverageResult;
+ data['MonthSystolicePressureResult'] = monthSystolicePressureResult;
+ data['WeekSystolicePressureResult'] = weekSystolicePressureResult;
+ data['YearDiastolicPressureAverageResult'] = yearDiastolicPressureAverageResult;
+ data['MonthDiastolicPressureResult'] = monthDiastolicPressureResult;
+ data['WeekDiastolicPressureResult'] = weekDiastolicPressureResult;
+ data['MeasuredArmDesc'] = measuredArmDesc;
+ data['WeekDesc'] = weekDesc;
+ return data;
+ }
+}
+
diff --git a/lib/features/health_trackers/models/blood_pressure/month_blood_pressure_result_average.dart b/lib/features/health_trackers/models/blood_pressure/month_blood_pressure_result_average.dart
new file mode 100644
index 0000000..3975805
--- /dev/null
+++ b/lib/features/health_trackers/models/blood_pressure/month_blood_pressure_result_average.dart
@@ -0,0 +1,58 @@
+class MonthBloodPressureResultAverage {
+ dynamic weekfourSystolicePressureAverageResult;
+ dynamic weekfourDiastolicPressureAverageResult;
+ dynamic weekthreeSystolicePressureAverageResult;
+ dynamic weekthreeDiastolicPressureAverageResult;
+ dynamic weektwoSystolicePressureAverageResult;
+ dynamic weektwoDiastolicPressureAverageResult;
+ dynamic weekoneSystolicePressureAverageResult;
+ dynamic weekoneDiastolicPressureAverageResult;
+ String? weekDesc;
+ int? weekDiastolicPressureAverageResult;
+ int? weekSystolicePressureAverageResult;
+
+ MonthBloodPressureResultAverage({
+ this.weekfourSystolicePressureAverageResult,
+ this.weekfourDiastolicPressureAverageResult,
+ this.weekthreeSystolicePressureAverageResult,
+ this.weekthreeDiastolicPressureAverageResult,
+ this.weektwoSystolicePressureAverageResult,
+ this.weektwoDiastolicPressureAverageResult,
+ this.weekoneSystolicePressureAverageResult,
+ this.weekoneDiastolicPressureAverageResult,
+ this.weekDesc,
+ this.weekDiastolicPressureAverageResult,
+ this.weekSystolicePressureAverageResult,
+ });
+
+ MonthBloodPressureResultAverage.fromJson(Map json) {
+ weekfourSystolicePressureAverageResult = json['weekfourSystolicePressureAverageResult'];
+ weekfourDiastolicPressureAverageResult = json['weekfourDiastolicPressureAverageResult'];
+ weekthreeSystolicePressureAverageResult = json['weekthreeSystolicePressureAverageResult'];
+ weekthreeDiastolicPressureAverageResult = json['weekthreeDiastolicPressureAverageResult'];
+ weektwoSystolicePressureAverageResult = json['weektwoSystolicePressureAverageResult'];
+ weektwoDiastolicPressureAverageResult = json['weektwoDiastolicPressureAverageResult'];
+ weekoneSystolicePressureAverageResult = json['weekoneSystolicePressureAverageResult'];
+ weekoneDiastolicPressureAverageResult = json['weekoneDiastolicPressureAverageResult'];
+ weekDesc = json['WeekDesc'];
+ weekDiastolicPressureAverageResult = json['WeekDiastolicPressureAverageResult'];
+ weekSystolicePressureAverageResult = json['WeekSystolicePressureAverageResult'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['weekfourSystolicePressureAverageResult'] = weekfourSystolicePressureAverageResult;
+ data['weekfourDiastolicPressureAverageResult'] = weekfourDiastolicPressureAverageResult;
+ data['weekthreeSystolicePressureAverageResult'] = weekthreeSystolicePressureAverageResult;
+ data['weekthreeDiastolicPressureAverageResult'] = weekthreeDiastolicPressureAverageResult;
+ data['weektwoSystolicePressureAverageResult'] = weektwoSystolicePressureAverageResult;
+ data['weektwoDiastolicPressureAverageResult'] = weektwoDiastolicPressureAverageResult;
+ data['weekoneSystolicePressureAverageResult'] = weekoneSystolicePressureAverageResult;
+ data['weekoneDiastolicPressureAverageResult'] = weekoneDiastolicPressureAverageResult;
+ data['WeekDesc'] = weekDesc;
+ data['WeekDiastolicPressureAverageResult'] = weekDiastolicPressureAverageResult;
+ data['WeekSystolicePressureAverageResult'] = weekSystolicePressureAverageResult;
+ return data;
+ }
+}
+
diff --git a/lib/features/health_trackers/models/blood_pressure/week_blood_pressure_result_average.dart b/lib/features/health_trackers/models/blood_pressure/week_blood_pressure_result_average.dart
new file mode 100644
index 0000000..381d514
--- /dev/null
+++ b/lib/features/health_trackers/models/blood_pressure/week_blood_pressure_result_average.dart
@@ -0,0 +1,24 @@
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+
+class WeekBloodPressureResultAverage {
+ int? dailySystolicePressureAverageResult;
+ int? dailyDiastolicPressureAverageResult;
+ DateTime? bloodPressureDate;
+
+ WeekBloodPressureResultAverage({this.dailySystolicePressureAverageResult, this.dailyDiastolicPressureAverageResult, this.bloodPressureDate});
+
+ WeekBloodPressureResultAverage.fromJson(Map json) {
+ dailySystolicePressureAverageResult = json['DailySystolicePressureAverageResult'];
+ dailyDiastolicPressureAverageResult = json['DailyDiastolicPressureAverageResult'];
+ bloodPressureDate = DateUtil.convertStringToDate(json['BloodPressureDate']);
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['DailySystolicePressureAverageResult'] = dailySystolicePressureAverageResult;
+ data['DailyDiastolicPressureAverageResult'] = dailyDiastolicPressureAverageResult;
+ data['BloodPressureDate'] = bloodPressureDate;
+ return data;
+ }
+}
+
diff --git a/lib/features/health_trackers/models/blood_pressure/year_blood_pressure_result_average.dart b/lib/features/health_trackers/models/blood_pressure/year_blood_pressure_result_average.dart
new file mode 100644
index 0000000..6f34246
--- /dev/null
+++ b/lib/features/health_trackers/models/blood_pressure/year_blood_pressure_result_average.dart
@@ -0,0 +1,37 @@
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+
+class YearBloodPressureResultAverage {
+ int? monthSystolicePressureAverageResult;
+ int? monthDiastolicPressureAverageResult;
+ dynamic monthNumber;
+ String? monthName;
+ String? yearName;
+ DateTime? date;
+
+ YearBloodPressureResultAverage({
+ this.monthSystolicePressureAverageResult,
+ this.monthDiastolicPressureAverageResult,
+ this.monthNumber,
+ this.monthName,
+ this.yearName,
+ });
+
+ YearBloodPressureResultAverage.fromJson(Map json) {
+ monthSystolicePressureAverageResult = json['monthSystolicePressureAverageResult'];
+ monthDiastolicPressureAverageResult = json['monthDiastolicPressureAverageResult'];
+ monthNumber = json['monthNumber'];
+ monthName = json['monthName'];
+ yearName = json['yearName'];
+ date = DateUtil.getMonthDateTime(monthName!, yearName);
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['monthSystolicePressureAverageResult'] = monthSystolicePressureAverageResult;
+ data['monthDiastolicPressureAverageResult'] = monthDiastolicPressureAverageResult;
+ data['monthNumber'] = monthNumber;
+ data['monthName'] = monthName;
+ data['yearName'] = yearName;
+ return data;
+ }
+}
diff --git a/lib/features/health_trackers/models/blood_sugar/diabetic_patient_result.dart b/lib/features/health_trackers/models/blood_sugar/diabetic_patient_result.dart
new file mode 100644
index 0000000..066df3c
--- /dev/null
+++ b/lib/features/health_trackers/models/blood_sugar/diabetic_patient_result.dart
@@ -0,0 +1,99 @@
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+
+class DiabeticPatientResult {
+ String? chartMonth;
+ var chartYear;
+ DateTime? dateChart;
+ var description;
+ var descriptionN;
+ int? diabtecAvarage;
+ bool? isActive;
+ int? lineItemNo;
+ var listMonth;
+ var listWeek;
+ int? measured;
+ String? measuredDesc;
+ var monthAverageResult;
+ int? patientID;
+ var remark;
+ var resultDesc;
+ dynamic resultValue;
+ String? unit;
+ var weekAverageResult;
+ String? weekDesc;
+ var yearAverageResult;
+
+ DiabeticPatientResult(
+ {this.chartMonth,
+ this.chartYear,
+ this.dateChart,
+ this.description,
+ this.descriptionN,
+ this.diabtecAvarage,
+ this.isActive,
+ this.lineItemNo,
+ this.listMonth,
+ this.listWeek,
+ this.measured,
+ this.measuredDesc,
+ this.monthAverageResult,
+ this.patientID,
+ this.remark,
+ this.resultDesc,
+ this.resultValue,
+ this.unit,
+ this.weekAverageResult,
+ this.weekDesc,
+ this.yearAverageResult});
+
+ DiabeticPatientResult.fromJson(Map json) {
+ chartMonth = json['ChartMonth'];
+ chartYear = json['ChartYear'];
+ dateChart = DateUtil.convertStringToDate(json['DateChart']);
+ description = json['Description'];
+ descriptionN = json['DescriptionN'];
+ diabtecAvarage = json['DiabtecAvarage'];
+ isActive = json['IsActive'];
+ lineItemNo = json['LineItemNo'];
+ listMonth = json['List_Month'];
+ listWeek = json['List_Week'];
+ measured = json['Measured'];
+ measuredDesc = json['MeasuredDesc'];
+ monthAverageResult = json['MonthAverageResult'];
+ patientID = json['PatientID'];
+ remark = json['Remark'];
+ resultDesc = json['ResultDesc'];
+ resultValue = json['ResultValue'];
+ unit = json['Unit'];
+ weekAverageResult = json['WeekAverageResult'];
+ weekDesc = json['WeekDesc'];
+ yearAverageResult = json['YearAverageResult'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['ChartMonth'] = chartMonth;
+ data['ChartYear'] = chartYear;
+ data['DateChart'] = DateUtil.convertDateToString(dateChart!);
+ data['Description'] = description;
+ data['DescriptionN'] = descriptionN;
+ data['DiabtecAvarage'] = diabtecAvarage;
+ data['IsActive'] = isActive;
+ data['LineItemNo'] = lineItemNo;
+ data['List_Month'] = listMonth;
+ data['List_Week'] = listWeek;
+ data['Measured'] = measured;
+ data['MeasuredDesc'] = measuredDesc;
+ data['MonthAverageResult'] = monthAverageResult;
+ data['PatientID'] = patientID;
+ data['Remark'] = remark;
+ data['ResultDesc'] = resultDesc;
+ data['ResultValue'] = resultValue;
+ data['Unit'] = unit;
+ data['WeekAverageResult'] = weekAverageResult;
+ data['WeekDesc'] = weekDesc;
+ data['YearAverageResult'] = yearAverageResult;
+ return data;
+ }
+}
+
diff --git a/lib/features/health_trackers/models/blood_sugar/month_diabetic_result_average.dart b/lib/features/health_trackers/models/blood_sugar/month_diabetic_result_average.dart
new file mode 100644
index 0000000..77b06f1
--- /dev/null
+++ b/lib/features/health_trackers/models/blood_sugar/month_diabetic_result_average.dart
@@ -0,0 +1,37 @@
+class MonthDiabeticResultAverage {
+ var weekfourAverageResult;
+ var weekthreeAverageResult;
+ var weektwoAverageResult;
+ var weekoneAverageResult;
+ dynamic weekAverageResult;
+ String? weekDesc;
+
+ MonthDiabeticResultAverage(
+ {this.weekfourAverageResult,
+ this.weekthreeAverageResult,
+ this.weektwoAverageResult,
+ this.weekoneAverageResult,
+ this.weekAverageResult,
+ this.weekDesc});
+
+ MonthDiabeticResultAverage.fromJson(Map json) {
+ weekfourAverageResult = json['weekfourAverageResult'];
+ weekthreeAverageResult = json['weekthreeAverageResult'];
+ weektwoAverageResult = json['weektwoAverageResult'];
+ weekoneAverageResult = json['weekoneAverageResult'];
+ weekAverageResult = json['WeekAverageResult'];
+ weekDesc = json['WeekDesc'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['weekfourAverageResult'] = weekfourAverageResult;
+ data['weekthreeAverageResult'] = weekthreeAverageResult;
+ data['weektwoAverageResult'] = weektwoAverageResult;
+ data['weekoneAverageResult'] = weekoneAverageResult;
+ data['WeekAverageResult'] = weekAverageResult;
+ data['WeekDesc'] = weekDesc;
+ return data;
+ }
+}
+
diff --git a/lib/features/health_trackers/models/blood_sugar/week_diabetic_result_average.dart b/lib/features/health_trackers/models/blood_sugar/week_diabetic_result_average.dart
new file mode 100644
index 0000000..3b35fe7
--- /dev/null
+++ b/lib/features/health_trackers/models/blood_sugar/week_diabetic_result_average.dart
@@ -0,0 +1,21 @@
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+
+class WeekDiabeticResultAverage {
+ dynamic dailyAverageResult;
+ DateTime? dateChart;
+
+ WeekDiabeticResultAverage({this.dailyAverageResult, this.dateChart});
+
+ WeekDiabeticResultAverage.fromJson(Map json) {
+ dailyAverageResult = json['DailyAverageResult'];
+ dateChart = DateUtil.convertStringToDate(json['DateChart']);
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['DailyAverageResult'] = dailyAverageResult;
+ data['DateChart'] = DateUtil.convertDateToString(dateChart!);
+ return data;
+ }
+}
+
diff --git a/lib/features/health_trackers/models/blood_sugar/year_diabetic_result_average.dart b/lib/features/health_trackers/models/blood_sugar/year_diabetic_result_average.dart
new file mode 100644
index 0000000..fb34056
--- /dev/null
+++ b/lib/features/health_trackers/models/blood_sugar/year_diabetic_result_average.dart
@@ -0,0 +1,35 @@
+import 'dart:developer';
+
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+
+class YearDiabeticResultAverage {
+ dynamic monthAverageResult;
+ var monthNumber;
+ String? monthName;
+ String? yearName;
+ DateTime? date;
+
+ YearDiabeticResultAverage({this.monthAverageResult, this.monthNumber, this.monthName, this.yearName});
+
+ YearDiabeticResultAverage.fromJson(Map json) {
+ try {
+ monthAverageResult = json['monthAverageResult'];
+ monthNumber = json['monthNumber'];
+ monthName = json['monthName'];
+ yearName = json['yearName'];
+ date = DateUtil.getMonthDateTime(monthName!, yearName);
+ } catch (e) {
+ log(e.toString());
+ }
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['monthAverageResult'] = monthAverageResult;
+ data['monthNumber'] = monthNumber;
+ data['monthName'] = monthName;
+ data['yearName'] = yearName;
+ return data;
+ }
+}
+
diff --git a/lib/features/health_trackers/models/weight/month_weight_measurement_result_average.dart b/lib/features/health_trackers/models/weight/month_weight_measurement_result_average.dart
new file mode 100644
index 0000000..f95470c
--- /dev/null
+++ b/lib/features/health_trackers/models/weight/month_weight_measurement_result_average.dart
@@ -0,0 +1,37 @@
+class MonthWeightMeasurementResultAverage {
+ dynamic weekfourAverageResult;
+ dynamic weekthreeAverageResult;
+ dynamic weektwoAverageResult;
+ dynamic weekoneAverageResult;
+ dynamic weekAverageResult;
+ String? weekDesc;
+
+ MonthWeightMeasurementResultAverage(
+ {this.weekfourAverageResult,
+ this.weekthreeAverageResult,
+ this.weektwoAverageResult,
+ this.weekoneAverageResult,
+ this.weekAverageResult,
+ this.weekDesc});
+
+ MonthWeightMeasurementResultAverage.fromJson(Map json) {
+ weekfourAverageResult = json['weekfourAverageResult'];
+ weekthreeAverageResult = json['weekthreeAverageResult'];
+ weektwoAverageResult = json['weektwoAverageResult'];
+ weekoneAverageResult = json['weekoneAverageResult'];
+ weekAverageResult = json['WeekAverageResult'];
+ weekDesc = json['WeekDesc'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['weekfourAverageResult'] = weekfourAverageResult;
+ data['weekthreeAverageResult'] = weekthreeAverageResult;
+ data['weektwoAverageResult'] = weektwoAverageResult;
+ data['weekoneAverageResult'] = weekoneAverageResult;
+ data['WeekAverageResult'] = weekAverageResult;
+ data['WeekDesc'] = weekDesc;
+ return data;
+ }
+}
+
diff --git a/lib/features/health_trackers/models/weight/week_weight_measurement_result_average.dart b/lib/features/health_trackers/models/weight/week_weight_measurement_result_average.dart
new file mode 100644
index 0000000..ad53325
--- /dev/null
+++ b/lib/features/health_trackers/models/weight/week_weight_measurement_result_average.dart
@@ -0,0 +1,21 @@
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+
+class WeekWeightMeasurementResultAverage {
+ dynamic dailyAverageResult;
+ DateTime? weightDate;
+
+ WeekWeightMeasurementResultAverage({this.dailyAverageResult, this.weightDate});
+
+ WeekWeightMeasurementResultAverage.fromJson(Map json) {
+ dailyAverageResult = json['DailyAverageResult'];
+ weightDate = DateUtil.convertStringToDate(json['WeightDate']);
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['DailyAverageResult'] = dailyAverageResult;
+ data['WeightDate'] = weightDate;
+ return data;
+ }
+}
+
diff --git a/lib/features/health_trackers/models/weight/weight_measurement_result.dart b/lib/features/health_trackers/models/weight/weight_measurement_result.dart
new file mode 100644
index 0000000..896d33f
--- /dev/null
+++ b/lib/features/health_trackers/models/weight/weight_measurement_result.dart
@@ -0,0 +1,77 @@
+import 'dart:developer';
+
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+
+class WeightMeasurementResult {
+ int? patientID;
+ int? lineItemNo;
+ int? weightMeasured;
+ DateTime? weightDate;
+ dynamic remark;
+ bool? isActive;
+ int? measured;
+ dynamic unit;
+ int? chartYear;
+ dynamic chartMonth;
+ double? yearAverageResult;
+ dynamic monthAverageResult;
+ dynamic weekAverageResult;
+ dynamic weekDesc;
+
+ WeightMeasurementResult(
+ {this.patientID,
+ this.lineItemNo,
+ this.weightMeasured,
+ this.weightDate,
+ this.remark,
+ this.isActive,
+ this.measured,
+ this.unit,
+ this.chartYear,
+ this.chartMonth,
+ this.yearAverageResult,
+ this.monthAverageResult,
+ this.weekAverageResult,
+ this.weekDesc});
+
+ WeightMeasurementResult.fromJson(Map json) {
+ try {
+ patientID = json['PatientID'];
+ lineItemNo = json['LineItemNo'];
+ weightMeasured = json['WeightMeasured'];
+ weightDate = DateUtil.convertStringToDate(json['WeightDate']);
+ remark = json['Remark'];
+ isActive = json['IsActive'];
+ measured = json['Measured'];
+ unit = json['Unit'];
+ chartYear = json['ChartYear'];
+ chartMonth = json['ChartMonth'];
+ // Convert to double safely since API may return int
+ yearAverageResult = json['YearAverageResult'] != null ? (json['YearAverageResult'] as num).toDouble() : null;
+ monthAverageResult = json['MonthAverageResult'];
+ weekAverageResult = json['WeekAverageResult'];
+ weekDesc = json['WeekDesc'];
+ } catch (e) {
+ log(e.toString());
+ }
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['PatientID'] = patientID;
+ data['LineItemNo'] = lineItemNo;
+ data['WeightMeasured'] = weightMeasured;
+ data['WeightDate'] = weightDate;
+ data['Remark'] = remark;
+ data['IsActive'] = isActive;
+ data['Measured'] = measured;
+ data['Unit'] = unit;
+ data['ChartYear'] = chartYear;
+ data['ChartMonth'] = chartMonth;
+ data['YearAverageResult'] = yearAverageResult;
+ data['MonthAverageResult'] = monthAverageResult;
+ data['WeekAverageResult'] = weekAverageResult;
+ data['WeekDesc'] = weekDesc;
+ return data;
+ }
+}
diff --git a/lib/features/health_trackers/models/weight/year_weight_measurement_result_average.dart b/lib/features/health_trackers/models/weight/year_weight_measurement_result_average.dart
new file mode 100644
index 0000000..7dc4b24
--- /dev/null
+++ b/lib/features/health_trackers/models/weight/year_weight_measurement_result_average.dart
@@ -0,0 +1,29 @@
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+
+class YearWeightMeasurementResultAverage {
+ dynamic monthAverageResult;
+ int? monthNumber;
+ String? monthName;
+ String? yearName;
+ DateTime? date;
+
+ YearWeightMeasurementResultAverage({this.monthAverageResult, this.monthNumber, this.monthName, this.yearName});
+
+ YearWeightMeasurementResultAverage.fromJson(Map json) {
+ monthAverageResult = json['monthAverageResult'];
+ monthNumber = json['monthNumber'];
+ monthName = json['monthName'];
+ yearName = json['yearName'];
+ date = DateUtil.getMonthDateTime(monthName!, yearName);
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['monthAverageResult'] = monthAverageResult;
+ data['monthNumber'] = monthNumber;
+ data['monthName'] = monthName;
+ data['yearName'] = yearName;
+ return data;
+ }
+}
+
diff --git a/lib/features/water_monitor/water_monitor_view_model.dart b/lib/features/water_monitor/water_monitor_view_model.dart
index 18ffddd..d82712a 100644
--- a/lib/features/water_monitor/water_monitor_view_model.dart
+++ b/lib/features/water_monitor/water_monitor_view_model.dart
@@ -16,14 +16,19 @@ import 'package:hmg_patient_app_new/features/water_monitor/models/water_cup_mode
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_repo.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
+import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/services/notification_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class WaterMonitorViewModel extends ChangeNotifier {
WaterMonitorRepo waterMonitorRepo;
+ ErrorHandlerService errorHandlerService;
- WaterMonitorViewModel({required this.waterMonitorRepo});
+ WaterMonitorViewModel({
+ required this.waterMonitorRepo,
+ required this.errorHandlerService,
+ });
// Controllers
final TextEditingController nameController = TextEditingController();
@@ -190,9 +195,9 @@ class WaterMonitorViewModel extends ChangeNotifier {
identificationNo: identification,
);
- result.fold((failure) {
- log('Error fetching user progress: ${failure.message}');
- }, (apiModel) {
+ result.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) {
log("User Progress Data ($_selectedDuration): ${apiModel.data.toString()}");
// Parse the response based on progress type
@@ -300,10 +305,13 @@ class WaterMonitorViewModel extends ChangeNotifier {
identificationNo: identification,
);
- result.fold((failure) {
- _userDetailData = null;
- if (onError != null) onError(failure.message);
- }, (apiModel) {
+ result.fold(
+ (failure) {
+ errorHandlerService.handleError(failure: failure);
+ _userDetailData = null;
+ if (onError != null) onError(failure.message);
+ },
+ (apiModel) {
_userDetailData = apiModel.data;
// Populate form fields from the fetched data
@@ -322,7 +330,7 @@ class WaterMonitorViewModel extends ChangeNotifier {
if (_userDetailData == null) {
try {
- _navigationService.pushAndReplace(AppRoutes.waterMonitorSettingsScreen);
+ _navigationService.pushAndReplace(AppRoutes.waterMonitorSettingsPage);
} catch (navErr) {
log('Navigation to water monitor settings failed: $navErr');
}
@@ -686,6 +694,7 @@ class WaterMonitorViewModel extends ChangeNotifier {
return result.fold(
(failure) {
+ errorHandlerService.handleError(failure: failure);
_validationError = failure.message;
_isLoading = false;
notifyListeners();
@@ -1060,6 +1069,7 @@ class WaterMonitorViewModel extends ChangeNotifier {
return result.fold(
(failure) {
+ errorHandlerService.handleError(failure: failure);
log('Error inserting user activity: ${failure.message}');
_isLoading = false;
notifyListeners();
@@ -1134,6 +1144,7 @@ class WaterMonitorViewModel extends ChangeNotifier {
return result.fold(
(failure) {
+ errorHandlerService.handleError(failure: failure);
log('Error undoing user activity: ${failure.message}');
_isLoading = false;
notifyListeners();
diff --git a/lib/main.dart b/lib/main.dart
index a9d16dc..f0537b3 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -35,6 +35,7 @@ import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_v
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_view_model.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_view_model.dart';
+import 'package:hmg_patient_app_new/presentation/health_trackers/health_trackers_view_model.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
@@ -173,6 +174,9 @@ void main() async {
),
ChangeNotifierProvider(
create: (_) => getIt.get(),
+ ),
+ ChangeNotifierProvider(
+ create: (_) => getIt.get(),
)
], child: MyApp()),
),
diff --git a/lib/presentation/health_calculators_and_converts/health_calculator_detailed_page.dart b/lib/presentation/health_calculators_and_converts/health_calculator_detailed_page.dart
index eb22ab6..42cba5d 100644
--- a/lib/presentation/health_calculators_and_converts/health_calculator_detailed_page.dart
+++ b/lib/presentation/health_calculators_and_converts/health_calculator_detailed_page.dart
@@ -1,4 +1,9 @@
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/presentation/book_appointment/select_clinic_page.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_view_model.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/bf.dart';
@@ -12,17 +17,12 @@ import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/ibw.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/ovulation.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/triglycerides.dart';
-import 'package:provider/provider.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/services/dialog_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
+import 'package:provider/provider.dart';
class HealthCalculatorDetailedPage extends StatefulWidget {
HealthCalculatorsTypeEnum calculatorType;
@@ -50,8 +50,8 @@ class _HealthCalculatorDetailedPageState extends State months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+ const List months = [
+ 'January',
+ 'February',
+ 'March',
+ 'April',
+ 'May',
+ 'June',
+ 'July',
+ 'August',
+ 'September',
+ 'October',
+ 'November',
+ 'December'
+ ];
return months[month - 1];
}
@@ -828,7 +846,8 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
// expose map-like results for widgets to forward to parent
Map? get bmiResultMap => bmiResult == null ? null : {'bmiResult': bmiResult, 'bmiCategory': bmiCategory};
- Map? get caloriesResultMap => caloriesResult == null ? null : {'calories': caloriesResult, 'bmr': bmrResult, 'calorieRange': calorieRange, 'bmrRange': bmrRange};
+ Map? get caloriesResultMap =>
+ caloriesResult == null ? null : {'calories': caloriesResult, 'bmr': bmrResult, 'calorieRange': calorieRange, 'bmrRange': bmrRange};
Map? get ibwResultMap => ibwResult == null ? null : {'ibw': ibwResult, 'difference': weightDifference, 'status': weightStatus};
diff --git a/lib/presentation/health_trackers/add_health_tracker_entry_page.dart b/lib/presentation/health_trackers/add_health_tracker_entry_page.dart
new file mode 100644
index 0000000..56f82fc
--- /dev/null
+++ b/lib/presentation/health_trackers/add_health_tracker_entry_page.dart
@@ -0,0 +1,567 @@
+import 'dart:developer';
+
+import 'package:flutter/material.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/dependencies.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/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/presentation/health_trackers/health_trackers_view_model.dart';
+import 'package:hmg_patient_app_new/services/dialog_service.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/input_widget.dart';
+import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
+import 'package:intl/intl.dart';
+import 'package:provider/provider.dart';
+
+class AddHealthTrackerEntryPage extends StatefulWidget {
+ final HealthTrackerTypeEnum trackerType;
+
+ const AddHealthTrackerEntryPage({
+ super.key,
+ required this.trackerType,
+ });
+
+ @override
+ State createState() => _AddHealthTrackerEntryPageState();
+}
+
+class _AddHealthTrackerEntryPageState extends State {
+ late DialogService dialogService;
+
+ // Controllers for date and time
+ final TextEditingController dateController = TextEditingController();
+ final TextEditingController timeController = TextEditingController();
+
+ @override
+ void initState() {
+ super.initState();
+ dialogService = getIt.get();
+ }
+
+ @override
+ void dispose() {
+ dateController.dispose();
+ timeController.dispose();
+ super.dispose();
+ }
+
+ /// Get page title based on tracker type
+ String _getPageTitle() {
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ return "Add Blood Sugar".needTranslation;
+ case HealthTrackerTypeEnum.bloodPressure:
+ return "Add Blood Pressure".needTranslation;
+ case HealthTrackerTypeEnum.weightTracker:
+ return "Add Weight".needTranslation;
+ }
+ }
+
+ /// Get success message based on tracker type
+ String _getSuccessMessage() {
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ return "Blood Sugar Data saved successfully".needTranslation;
+ case HealthTrackerTypeEnum.bloodPressure:
+ return "Blood Pressure Data saved successfully".needTranslation;
+ case HealthTrackerTypeEnum.weightTracker:
+ return "Weight Data saved successfully".needTranslation;
+ }
+ }
+
+ /// Save entry based on tracker type
+ Future _saveEntry(HealthTrackersViewModel viewModel) async {
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ await _saveBloodSugarEntry(viewModel);
+ break;
+ case HealthTrackerTypeEnum.bloodPressure:
+ await _saveBloodPressureEntry(viewModel);
+ break;
+ case HealthTrackerTypeEnum.weightTracker:
+ await _saveWeightEntry(viewModel);
+ break;
+ }
+ }
+
+ // Save Blood Sugar entry
+ Future _saveBloodSugarEntry(HealthTrackersViewModel viewModel) async {
+ LoaderBottomSheet.showLoader(loadingText: "Please wait".needTranslation);
+ // Combine date and time
+ final dateTime = "${dateController.text} ${timeController.text}";
+
+ // Call ViewModel method with callbacks
+ await viewModel.saveBloodSugarEntry(
+ dateTime: dateTime,
+ measureTime: viewModel.selectedBloodSugarMeasureTime,
+ onSuccess: () {
+ LoaderBottomSheet.hideLoader();
+ _showSuccessAndPop();
+ },
+ onFailure: (error) {
+ LoaderBottomSheet.hideLoader();
+ dialogService.showErrorBottomSheet(message: error);
+ },
+ );
+ }
+
+ // Save Weight entry
+ Future _saveWeightEntry(HealthTrackersViewModel viewModel) async {
+ LoaderBottomSheet.showLoader(loadingText: "Please wait".needTranslation);
+ // Combine date and time
+ final dateTime = "${dateController.text} ${timeController.text}";
+
+ // Call ViewModel method with callbacks
+ await viewModel.saveWeightEntry(
+ dateTime: dateTime,
+ onSuccess: () {
+ LoaderBottomSheet.hideLoader();
+ _showSuccessAndPop();
+ },
+ onFailure: (error) {
+ LoaderBottomSheet.hideLoader();
+ dialogService.showErrorBottomSheet(message: error);
+ },
+ );
+ }
+
+ // Save Blood Pressure entry
+ Future _saveBloodPressureEntry(HealthTrackersViewModel viewModel) async {
+ LoaderBottomSheet.showLoader(loadingText: "Please wait".needTranslation);
+ // Combine date and time
+ final dateTime = "${dateController.text} ${timeController.text}";
+
+ // Call ViewModel method with callbacks
+ await viewModel.saveBloodPressureEntry(
+ dateTime: dateTime,
+ onSuccess: () {
+ LoaderBottomSheet.hideLoader();
+ _showSuccessAndPop();
+ },
+ onFailure: (error) {
+ LoaderBottomSheet.hideLoader();
+ dialogService.showErrorBottomSheet(message: error);
+ },
+ );
+ }
+
+ // Show success message and pop back
+ void _showSuccessAndPop() {
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getSuccessWidget(
+ loadingText: _getSuccessMessage(),
+ ),
+ callBackFunc: () {
+ Navigator.pop(context);
+ },
+ isCloseButtonVisible: false,
+ isDismissible: true,
+ isFullScreen: false,
+ );
+ }
+
+ // Reusable method to build selection row widget
+ Widget _buildSelectionRow({
+ required String value,
+ required String groupValue,
+ required VoidCallback onTap,
+ bool useUpperCase = false,
+ }) {
+ return SizedBox(
+ height: 70.h,
+ child: Row(
+ spacing: 8.h,
+ children: [
+ Radio(
+ value: value,
+ groupValue: groupValue,
+ activeColor: AppColors.errorColor,
+ onChanged: (_) => onTap(),
+ materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
+ ),
+ (useUpperCase ? value.toUpperCase() : value.toCamelCase)
+ .toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1)
+ .expanded,
+ ],
+ ).onPress(onTap),
+ );
+ }
+
+ // Reusable method to show selection bottom sheet
+ void _showSelectionBottomSheet({
+ required BuildContext context,
+ required String title,
+ required List items,
+ required String selectedValue,
+ required Function(String) onSelected,
+ bool useUpperCase = false,
+ }) {
+ dialogService.showFamilyBottomSheetWithoutHWithChild(
+ label: title.needTranslation,
+ message: "",
+ child: Container(
+ constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.7),
+ padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
+ decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
+ child: ListView.separated(
+ shrinkWrap: true,
+ itemCount: items.length,
+ itemBuilder: (context, index) {
+ final item = items[index];
+ return _buildSelectionRow(
+ value: item,
+ groupValue: selectedValue,
+ useUpperCase: useUpperCase,
+ onTap: () {
+ onSelected(item);
+ Navigator.pop(context);
+ },
+ );
+ },
+ separatorBuilder: (_, __) => Divider(height: 1, color: AppColors.dividerColor),
+ ),
+ ),
+ onOkPressed: () {},
+ );
+ }
+
+ // Blood Sugar unit selection
+ void _showBloodSugarUnitSelectionBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
+ FocusScope.of(context).unfocus();
+ _showSelectionBottomSheet(
+ context: context,
+ title: "Select Unit".needTranslation,
+ items: viewModel.bloodSugarUnit,
+ selectedValue: viewModel.selectedBloodSugarUnit,
+ onSelected: viewModel.setBloodSugarUnit,
+ useUpperCase: false,
+ );
+ }
+
+ // Blood Sugar measure time selection
+ void _showBloodSugarEntryTimeBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
+ FocusScope.of(context).unfocus();
+ _showSelectionBottomSheet(
+ context: context,
+ title: "Select Measure Time".needTranslation,
+ items: viewModel.bloodSugarMeasureTimeEnList,
+ selectedValue: viewModel.selectedBloodSugarMeasureTime,
+ onSelected: viewModel.setBloodSugarMeasureTime,
+ useUpperCase: false,
+ );
+ }
+
+ // Weight unit selection
+ void _showWeightUnitSelectionBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
+ FocusScope.of(context).unfocus();
+ _showSelectionBottomSheet(
+ context: context,
+ title: "Select Unit".needTranslation,
+ items: viewModel.weightUnits,
+ selectedValue: viewModel.selectedWeightUnit,
+ onSelected: viewModel.setWeightUnit,
+ useUpperCase: false,
+ );
+ }
+
+ // Blood Pressure measured arm selection
+ void _showMeasuredArmSelectionBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
+ FocusScope.of(context).unfocus();
+ _showSelectionBottomSheet(
+ context: context,
+ title: "Select Arm".needTranslation,
+ items: viewModel.measuredArmList,
+ selectedValue: viewModel.selectedMeasuredArm,
+ onSelected: viewModel.setMeasuredArm,
+ useUpperCase: false,
+ );
+ }
+
+ // Reusable method to build text field
+ Widget _buildTextField(TextEditingController controller, String hintText, {TextInputType keyboardType = TextInputType.name}) {
+ return TextField(
+ controller: controller,
+ keyboardType: keyboardType,
+ maxLines: 1,
+ cursorHeight: 14.h,
+ textAlignVertical: TextAlignVertical.center,
+ decoration: InputDecoration(
+ border: InputBorder.none,
+ contentPadding: EdgeInsets.zero,
+ isCollapsed: true,
+ hintText: hintText,
+ hintStyle: const TextStyle(color: Colors.grey),
+ ),
+ style: TextStyle(
+ fontSize: 14.f,
+ fontWeight: FontWeight.w500,
+ color: AppColors.textColor,
+ ),
+ );
+ }
+
+ // Reusable method to build settings row
+ Widget _buildSettingsRow({
+ required String icon,
+ required String label,
+ String? value,
+ Widget? inputField,
+ String? unit,
+ VoidCallback? onUnitTap,
+ VoidCallback? onRowTap,
+ Color? iconColor,
+ bool showDivider = true,
+ }) {
+ return Column(
+ children: [
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Container(
+ height: 40.w,
+ width: 40.w,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.greyColor,
+ borderRadius: 10.r,
+ hasShadow: false,
+ ),
+ child: Center(child: Utils.buildSvgWithAssets(icon: icon, height: 22.w, width: 22.w, iconColor: iconColor)),
+ ),
+ SizedBox(width: 12.w),
+ Expanded(
+ flex: unit != null ? 2 : 1,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ label.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
+ if (inputField != null)
+ Container(
+ height: 20.w,
+ alignment: Alignment.centerLeft,
+ child: inputField,
+ )
+ else if (value != null && value.isNotEmpty)
+ value.toCamelCase.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
+ ],
+ ),
+ ),
+ if (unit != null) ...[
+ Container(
+ width: 1.w,
+ height: 30.w,
+ color: AppColors.dividerColor,
+ ).paddingOnly(right: 10.w),
+ Expanded(
+ child: Row(
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ "Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
+ unit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
+ ],
+ ),
+ SizedBox(width: 12.w),
+ Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
+ ],
+ ).onPress(onUnitTap ?? () {}),
+ ),
+ ] else if (onRowTap != null) ...[
+ Utils.buildSvgWithAssets(icon: AppAssets.arrow_down),
+ SizedBox(width: 8.w),
+ ],
+ ],
+ ).paddingSymmetrical(0.w, 16.w).onPress(onRowTap ?? () {}),
+ if (showDivider) Divider(height: 1, color: AppColors.dividerColor),
+ ],
+ );
+ }
+
+ /// Build form fields based on tracker type
+ Widget _buildFormFields(HealthTrackersViewModel viewModel) {
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ return _buildBloodSugarForm(viewModel);
+ case HealthTrackerTypeEnum.bloodPressure:
+ return _buildBloodPressureForm(viewModel);
+ case HealthTrackerTypeEnum.weightTracker:
+ return _buildWeightForm(viewModel);
+ }
+ }
+
+ /// Blood Sugar form fields
+ Widget _buildBloodSugarForm(HealthTrackersViewModel viewModel) {
+ return Column(
+ children: [
+ _buildSettingsRow(
+ icon: AppAssets.heightIcon,
+ label: "Enter Blood Sugar".needTranslation,
+ inputField: _buildTextField(viewModel.bloodSugarController, '', keyboardType: TextInputType.number),
+ unit: viewModel.selectedBloodSugarUnit,
+ onUnitTap: () => _showBloodSugarUnitSelectionBottomSheet(context, viewModel),
+ ),
+ _buildDateTimeFields(),
+ Divider(height: 1, color: AppColors.dividerColor),
+ _buildSettingsRow(
+ icon: AppAssets.weight_tracker_icon,
+ label: "Select Measure Time".needTranslation,
+ value: viewModel.selectedBloodSugarMeasureTime,
+ onRowTap: () => _showBloodSugarEntryTimeBottomSheet(context, viewModel),
+ ),
+ ],
+ );
+ }
+
+ /// Blood Pressure form fields
+ Widget _buildBloodPressureForm(HealthTrackersViewModel viewModel) {
+ return Column(
+ children: [
+ _buildSettingsRow(
+ icon: AppAssets.bloodPressureIcon,
+ iconColor: AppColors.greyTextColor,
+ label: "Enter Systolic Value".needTranslation,
+ inputField: _buildTextField(viewModel.systolicController, '', keyboardType: TextInputType.number),
+ ),
+ _buildSettingsRow(
+ icon: AppAssets.bloodPressureIcon,
+ iconColor: AppColors.greyTextColor,
+ label: "Enter Diastolic Value".needTranslation,
+ inputField: _buildTextField(viewModel.diastolicController, '', keyboardType: TextInputType.number),
+ ),
+ _buildSettingsRow(
+ icon: AppAssets.bodyIcon,
+ iconColor: AppColors.greyTextColor,
+ label: "Select Arm".needTranslation,
+ value: viewModel.selectedMeasuredArm,
+ onRowTap: () => _showMeasuredArmSelectionBottomSheet(context, viewModel),
+ ),
+ _buildDateTimeFields(),
+ ],
+ );
+ }
+
+ /// Weight form fields
+ Widget _buildWeightForm(HealthTrackersViewModel viewModel) {
+ return Column(
+ children: [
+ _buildSettingsRow(
+ icon: AppAssets.weightScale,
+ label: "Enter Weight".needTranslation,
+ inputField: _buildTextField(viewModel.weightController, '', keyboardType: TextInputType.number),
+ unit: viewModel.selectedWeightUnit,
+ onUnitTap: () => _showWeightUnitSelectionBottomSheet(context, viewModel),
+ ),
+ _buildDateTimeFields(),
+ ],
+ );
+ }
+
+ /// Common date and time fields
+ Widget _buildDateTimeFields() {
+ return Column(
+ children: [
+ TextInputWidget(
+ controller: dateController,
+ isReadOnly: true,
+ isArrowTrailing: true,
+ labelText: "Date",
+ hintText: "Select date".needTranslation,
+ focusNode: FocusNode(),
+ isEnable: true,
+ prefix: null,
+ isAllowRadius: true,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ padding: EdgeInsets.symmetric(vertical: 8.h),
+ leadingIcon: AppAssets.calendarGrey,
+ selectionType: SelectionTypeEnum.calendar,
+ isHideSwitcher: true,
+ onCalendarTypeChanged: (val) {},
+ onChange: (val) {
+ if (val == null) return;
+ try {
+ final parsedDate = DateTime.parse(val);
+ final formattedDate = DateFormat('dd MMM yyyy').format(parsedDate);
+ dateController.text = formattedDate;
+ log("date: $formattedDate");
+ } catch (e) {
+ dateController.text = val;
+ log("date: $val");
+ }
+ },
+ ),
+ TextInputWidget(
+ controller: timeController,
+ isReadOnly: true,
+ isArrowTrailing: true,
+ labelText: "Time",
+ hintText: "Select time".needTranslation,
+ focusNode: FocusNode(),
+ isEnable: true,
+ prefix: null,
+ isAllowRadius: true,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ padding: EdgeInsets.symmetric(vertical: 8.h),
+ leadingIcon: AppAssets.calendarGrey,
+ selectionType: SelectionTypeEnum.time,
+ isHideSwitcher: true,
+ onCalendarTypeChanged: (val) {},
+ onChange: (val) {
+ if (val == null) return;
+ timeController.text = val;
+ log("time: $val");
+ },
+ ),
+ ],
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final viewModel = context.watch();
+
+ return GestureDetector(
+ onTap: () {
+ FocusScope.of(context).unfocus();
+ },
+ child: Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: CollapsingListView(
+ title: _getPageTitle(),
+ bottomChild: Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(24.w),
+ child: CustomButton(
+ text: "Save".needTranslation,
+ onPressed: () async => await _saveEntry(viewModel),
+ borderRadius: 12.r,
+ padding: EdgeInsets.symmetric(vertical: 14.h),
+ ),
+ ),
+ ),
+ child: Container(
+ margin: EdgeInsets.symmetric(horizontal: 24.w, vertical: 24.h),
+ padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
+ child: _buildFormFields(viewModel),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/health_trackers/health_tracker_detail_page.dart b/lib/presentation/health_trackers/health_tracker_detail_page.dart
new file mode 100644
index 0000000..a2b82ec
--- /dev/null
+++ b/lib/presentation/health_trackers/health_tracker_detail_page.dart
@@ -0,0 +1,1126 @@
+import 'package:fl_chart/fl_chart.dart';
+import 'package:flutter/material.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/common_models/data_points.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/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/health_trackers/models/blood_pressure/week_blood_pressure_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/blood_pressure/year_blood_pressure_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/blood_sugar/week_diabetic_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/blood_sugar/year_diabetic_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/weight/week_weight_measurement_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/weight/year_weight_measurement_result_average.dart';
+import 'package:hmg_patient_app_new/presentation/health_trackers/health_trackers_view_model.dart';
+import 'package:hmg_patient_app_new/presentation/health_trackers/widgets/tracker_last_value_card.dart';
+import 'package:hmg_patient_app_new/services/dialog_service.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
+import 'package:hmg_patient_app_new/widgets/graph/custom_graph.dart';
+import 'package:provider/provider.dart';
+import 'package:shimmer/shimmer.dart';
+
+class HealthTrackerDetailPage extends StatefulWidget {
+ final HealthTrackerTypeEnum trackerType;
+
+ const HealthTrackerDetailPage({super.key, required this.trackerType});
+
+ @override
+ State createState() => _HealthTrackerDetailPageState();
+}
+
+class _HealthTrackerDetailPageState extends State {
+ @override
+ void initState() {
+ super.initState();
+ // Load data based on tracker type
+ WidgetsBinding.instance.addPostFrameCallback((_) async {
+ final viewModel = context.read();
+ await _loadTrackerData(viewModel);
+ });
+ }
+
+ /// Load data based on tracker type
+ Future _loadTrackerData(HealthTrackersViewModel viewModel) async {
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ await viewModel.getBloodSugar();
+ break;
+ case HealthTrackerTypeEnum.bloodPressure:
+ await viewModel.getBloodPressure();
+ break;
+ case HealthTrackerTypeEnum.weightTracker:
+ await viewModel.getWeight();
+ break;
+ }
+ }
+
+ /// Get page title based on tracker type
+ String _getPageTitle() {
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ return "Blood Sugar".needTranslation;
+ case HealthTrackerTypeEnum.bloodPressure:
+ return "Blood Pressure".needTranslation;
+ case HealthTrackerTypeEnum.weightTracker:
+ return "Weight".needTranslation;
+ }
+ }
+
+ /// Get unit based on tracker type
+ String _getUnit() {
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ return 'mg/dL';
+ case HealthTrackerTypeEnum.bloodPressure:
+ return 'mmHg';
+ case HealthTrackerTypeEnum.weightTracker:
+ return 'kg';
+ }
+ }
+
+ /// Get empty state message based on tracker type
+ String _getEmptyStateMessage() {
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ return "Please add data to track your Blood Sugar";
+ case HealthTrackerTypeEnum.bloodPressure:
+ return "Please add data to track your Blood Pressure";
+ case HealthTrackerTypeEnum.weightTracker:
+ return "Please add data to track your Weight";
+ }
+ }
+
+ /// Check if data is empty based on tracker type
+ bool _hasNoData(HealthTrackersViewModel viewModel) {
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ return viewModel.weekDiabeticPatientResult.isEmpty &&
+ viewModel.monthDiabeticPatientResult.isEmpty &&
+ viewModel.yearDiabeticPatientResult.isEmpty;
+ case HealthTrackerTypeEnum.bloodPressure:
+ return viewModel.weekBloodPressureResult.isEmpty && viewModel.monthBloodPressureResult.isEmpty && viewModel.yearBloodPressureResult.isEmpty;
+ case HealthTrackerTypeEnum.weightTracker:
+ return viewModel.weekWeightMeasurementResult.isEmpty &&
+ viewModel.monthWeightMeasurementResult.isEmpty &&
+ viewModel.yearWeightMeasurementResult.isEmpty;
+ }
+ }
+
+ // Reusable method to build selection row widget
+ Widget _buildSelectionRow({
+ required String value,
+ required String groupValue,
+ required VoidCallback onTap,
+ bool useUpperCase = false,
+ }) {
+ return SizedBox(
+ height: 70.h,
+ child: Row(
+ spacing: 8.h,
+ children: [
+ Radio(
+ value: value,
+ groupValue: groupValue,
+ activeColor: AppColors.errorColor,
+ onChanged: (_) => onTap(),
+ materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
+ ),
+ (useUpperCase ? value.toUpperCase() : value.toCamelCase)
+ .toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1)
+ .expanded,
+ ],
+ ).onPress(onTap),
+ );
+ }
+
+ void _showSelectionBottomSheet({
+ required BuildContext context,
+ required String title,
+ required List items,
+ required String selectedValue,
+ required Function(String) onSelected,
+ bool useUpperCase = false,
+ }) {
+ final dialogService = getIt.get();
+
+ dialogService.showFamilyBottomSheetWithoutHWithChild(
+ label: title.needTranslation,
+ message: "",
+ child: Container(
+ padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
+ decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(20.r)),
+ child: ListView.separated(
+ shrinkWrap: true,
+ itemCount: items.length,
+ itemBuilder: (context, index) {
+ final item = items[index];
+ return _buildSelectionRow(
+ value: item,
+ groupValue: selectedValue,
+ useUpperCase: useUpperCase,
+ onTap: () {
+ onSelected(item);
+ Navigator.pop(context);
+ },
+ );
+ },
+ separatorBuilder: (_, __) => Divider(height: 1, color: AppColors.dividerColor),
+ ),
+ ),
+ onOkPressed: () {},
+ );
+ }
+
+ void _showHistoryDurationBottomsheet(BuildContext context, HealthTrackersViewModel viewModel) {
+ _showSelectionBottomSheet(
+ context: context,
+ title: "Select Duration".needTranslation,
+ items: viewModel.durationFilters,
+ selectedValue: viewModel.selectedDurationFilter,
+ onSelected: viewModel.setFilterDuration,
+ );
+ }
+
+ Widget buildHistoryListTile({required String title, required String subTitle, required String measureDesc, double? value}) {
+ // Get status color and rotation based on value and tracker type
+ Color statusColor = AppColors.successColor;
+ double rotation = 0;
+
+ if (value != null) {
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ if (value < 70) {
+ statusColor = AppColors.errorColor;
+ rotation = 0; // pointing down
+ } else if (value <= 100) {
+ statusColor = AppColors.successColor;
+ rotation = -3.14159 / 2; // pointing right
+ } else if (value <= 125) {
+ statusColor = AppColors.ratingColorYellow;
+ rotation = 3.14159; // pointing up
+ } else {
+ statusColor = AppColors.errorColor;
+ rotation = 3.14159; // pointing up
+ }
+ break;
+ case HealthTrackerTypeEnum.bloodPressure:
+ // Systolic pressure ranges
+ if (value < 90) {
+ statusColor = AppColors.errorColor;
+ rotation = 0; // Low - pointing down
+ } else if (value <= 120) {
+ statusColor = AppColors.successColor;
+ rotation = -3.14159 / 2; // Normal - pointing right
+ } else if (value <= 140) {
+ statusColor = AppColors.ratingColorYellow;
+ rotation = 3.14159; // Elevated - pointing up
+ } else {
+ statusColor = AppColors.errorColor;
+ rotation = 3.14159; // High - pointing up
+ }
+ break;
+ case HealthTrackerTypeEnum.weightTracker:
+ // Weight doesn't have good/bad indicators, just show neutral
+ statusColor = AppColors.transparent;
+ rotation = -3.14159 / 2; // pointing right (neutral)
+ break;
+ }
+ }
+
+ return Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ AppCustomChipWidget(labelText: title),
+ if (measureDesc.isNotEmpty) ...[
+ SizedBox(width: 8.w),
+ AppCustomChipWidget(labelText: measureDesc),
+ ],
+ ],
+ ),
+ SizedBox(height: 4.h),
+ subTitle.toText16(weight: FontWeight.w600, color: AppColors.textColor),
+ ],
+ ),
+ Transform.rotate(
+ angle: rotation,
+ child: Utils.buildSvgWithAssets(
+ icon: AppAssets.lowIndicatorIcon,
+ iconColor: statusColor,
+ height: 20.h,
+ width: 20.h,
+ ),
+ ),
+ ],
+ ).paddingSymmetrical(0, 8.h);
+ }
+
+ Widget _buildHistoryGraphOrList() {
+ return Container(
+ margin: EdgeInsets.symmetric(horizontal: 24.w),
+ padding: EdgeInsets.all(16.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: Consumer(builder: (BuildContext context, HealthTrackersViewModel viewModel, Widget? child) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ "History".needTranslation.toText16(isBold: true),
+ if (viewModel.isGraphView) ...[
+ SizedBox(width: 12.w),
+ InkWell(
+ onTap: () => _showHistoryDurationBottomsheet(context, viewModel),
+ child: Container(
+ padding: EdgeInsets.symmetric(vertical: 4.h, horizontal: 6.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ backgroundColor: AppColors.greyColor,
+ borderRadius: 8.r,
+ hasShadow: true,
+ ),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ viewModel.selectedDurationFilter.toText12(fontWeight: FontWeight.w500),
+ SizedBox(width: 4.w),
+ Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, height: 16.h),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ],
+ ),
+ InkWell(
+ onTap: () => viewModel.setGraphView(!viewModel.isGraphView),
+ child: AnimatedSwitcher(
+ duration: const Duration(milliseconds: 300),
+ transitionBuilder: (Widget child, Animation animation) {
+ return FadeTransition(
+ opacity: animation,
+ child: ScaleTransition(
+ scale: animation,
+ child: child,
+ ),
+ );
+ },
+ child: Container(
+ key: ValueKey(viewModel.isGraphView),
+ child: Utils.buildSvgWithAssets(
+ icon: viewModel.isGraphView ? AppAssets.listIcon : AppAssets.graphIcon,
+ height: 24.h,
+ width: 24.h,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ if (!viewModel.isGraphView) _buildHistoryListView(viewModel) else ...[SizedBox(height: 16.h), _buildHistoryGraph()]
+ ],
+ );
+ }),
+ );
+ }
+
+ String _formatTime(DateTime time) {
+ final hour = time.hour;
+ final minute = time.minute;
+ final hour12 = hour > 12 ? hour - 12 : (hour == 0 ? 12 : hour);
+ final period = hour >= 12 ? 'PM' : 'AM';
+ return '${hour12.toString().padLeft(2, '0')}:${minute.toString().padLeft(2, '0')} $period';
+ }
+
+ String _getDayName(DateTime date) {
+ const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
+ return days[date.weekday - 1];
+ }
+
+ String _getMonthName(int monthNumber) {
+ const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+ if (monthNumber < 1 || monthNumber > 12) return 'Unknown';
+ return months[monthNumber - 1];
+ }
+
+ Widget _buildHistoryListView(HealthTrackersViewModel viewModel) {
+ List listItems = [];
+ final unit = _getUnit();
+
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ listItems = _buildBloodSugarListItems(viewModel, unit);
+ break;
+ case HealthTrackerTypeEnum.bloodPressure:
+ listItems = _buildBloodPressureListItems(viewModel);
+ break;
+ case HealthTrackerTypeEnum.weightTracker:
+ listItems = _buildWeightListItems(viewModel, unit);
+ break;
+ }
+
+ if (viewModel.isLoading) {
+ return _buildLoadingShimmer().paddingOnly(top: 16.h);
+ }
+
+ if (listItems.isEmpty) {
+ return _buildEmptyStateWidget();
+ }
+
+ return ListView.separated(
+ padding: EdgeInsets.only(top: 16.h),
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: listItems.length,
+ separatorBuilder: (context, index) => SizedBox.shrink(),
+ itemBuilder: (context, index) => listItems[index],
+ );
+ }
+
+ /// Build list items for Blood Sugar
+ List _buildBloodSugarListItems(HealthTrackersViewModel viewModel, String unit) {
+ List listItems = [];
+ final allResults = [];
+
+ allResults.addAll(viewModel.weekDiabeticPatientResult);
+ allResults.addAll(viewModel.monthDiabeticPatientResult);
+ allResults.addAll(viewModel.yearDiabeticPatientResult);
+
+ final seenIds = {};
+ final uniqueResults = allResults.where((result) {
+ final id = '${result.lineItemNo}_${result.dateChart?.millisecondsSinceEpoch ?? 0}';
+ if (seenIds.contains(id)) return false;
+ seenIds.add(id);
+ return true;
+ }).toList();
+
+ uniqueResults.sort((a, b) {
+ final dateA = a.dateChart ?? DateTime(1900);
+ final dateB = b.dateChart ?? DateTime(1900);
+ return dateB.compareTo(dateA);
+ });
+
+ for (var result in uniqueResults) {
+ final resultValue = result.resultValue?.toDouble() ?? 0.0;
+ final value = result.resultValue?.toString() ?? '0';
+ final resultUnit = result.unit ?? unit;
+ final measuredDesc = result.measuredDesc ?? '';
+ final date = result.dateChart;
+ final dateLabel = date != null ? '${_getDayName(date)} ${date.day} ${_getMonthName(date.month).substring(0, 3)}, ${date.year}' : '';
+ final timeLabel = date != null ? _formatTime(date) : 'Unknown';
+ final displayLabel = date != null ? '$dateLabel, $timeLabel' : 'Unknown';
+ final subTitleText = '$value $resultUnit';
+
+ listItems.add(
+ Column(
+ children: [
+ buildHistoryListTile(
+ title: displayLabel,
+ subTitle: subTitleText,
+ value: resultValue,
+ measureDesc: measuredDesc,
+ ),
+ Divider(height: 1, color: AppColors.dividerColor).paddingOnly(bottom: 8.h),
+ ],
+ ),
+ );
+ }
+ return listItems;
+ }
+
+ /// Build list items for Blood Pressure
+ List _buildBloodPressureListItems(HealthTrackersViewModel viewModel) {
+ List listItems = [];
+ final allResults = [];
+
+ allResults.addAll(viewModel.weekBloodPressureResult);
+ allResults.addAll(viewModel.monthBloodPressureResult);
+ allResults.addAll(viewModel.yearBloodPressureResult);
+
+ final seenIds = {};
+ final uniqueResults = allResults.where((result) {
+ final id = '${result.lineItemNo}_${result.bloodPressureDate?.millisecondsSinceEpoch ?? 0}';
+ if (seenIds.contains(id)) return false;
+ seenIds.add(id);
+ return true;
+ }).toList();
+
+ uniqueResults.sort((a, b) {
+ final dateA = a.bloodPressureDate ?? DateTime(1900);
+ final dateB = b.bloodPressureDate ?? DateTime(1900);
+ return dateB.compareTo(dateA);
+ });
+
+ for (var result in uniqueResults) {
+ final systolic = result.systolicePressure ?? 0;
+ final diastolic = result.diastolicPressure ?? 0;
+ final measuredArmDesc = result.measuredArmDesc ?? '';
+ final date = result.bloodPressureDate;
+ final dateLabel = date != null ? '${_getDayName(date)} ${date.day} ${_getMonthName(date.month).substring(0, 3)}, ${date.year}' : '';
+ final timeLabel = date != null ? _formatTime(date) : 'Unknown';
+ final displayLabel = date != null ? '$dateLabel, $timeLabel' : 'Unknown';
+ final subTitleText = '$systolic/$diastolic mmHg';
+
+ listItems.add(
+ Column(
+ children: [
+ buildHistoryListTile(
+ title: displayLabel,
+ subTitle: subTitleText,
+ value: systolic.toDouble(),
+ measureDesc: measuredArmDesc,
+ ),
+ Divider(height: 1, color: AppColors.dividerColor).paddingOnly(bottom: 8.h),
+ ],
+ ),
+ );
+ }
+ return listItems;
+ }
+
+ /// Build list items for Weight
+ List _buildWeightListItems(HealthTrackersViewModel viewModel, String unit) {
+ List listItems = [];
+ final allResults = [];
+
+ allResults.addAll(viewModel.weekWeightMeasurementResult);
+ allResults.addAll(viewModel.monthWeightMeasurementResult);
+ allResults.addAll(viewModel.yearWeightMeasurementResult);
+
+ final seenIds = {};
+ final uniqueResults = allResults.where((result) {
+ final id = '${result.lineItemNo}_${result.weightDate?.millisecondsSinceEpoch ?? 0}';
+ if (seenIds.contains(id)) return false;
+ seenIds.add(id);
+ return true;
+ }).toList();
+
+ uniqueResults.sort((a, b) {
+ final dateA = a.weightDate ?? DateTime(1900);
+ final dateB = b.weightDate ?? DateTime(1900);
+ return dateB.compareTo(dateA);
+ });
+
+ for (var result in uniqueResults) {
+ final weightValue = result.weightMeasured?.toDouble() ?? 0.0;
+ final weightUnit = result.unit ?? unit;
+ final date = result.weightDate;
+ final dateLabel = date != null ? '${_getDayName(date)} ${date.day} ${_getMonthName(date.month).substring(0, 3)}, ${date.year}' : '';
+ final timeLabel = date != null ? _formatTime(date) : 'Unknown';
+ final displayLabel = date != null ? '$dateLabel, $timeLabel' : 'Unknown';
+ final subTitleText = '${weightValue.toInt()} $weightUnit';
+
+ listItems.add(
+ Column(
+ children: [
+ buildHistoryListTile(
+ title: displayLabel,
+ subTitle: subTitleText,
+ value: weightValue,
+ measureDesc: '',
+ ),
+ Divider(height: 1, color: AppColors.dividerColor).paddingOnly(bottom: 8.h),
+ ],
+ ),
+ );
+ }
+ return listItems;
+ }
+
+ Widget _buildLoadingShimmer({bool isForHistory = true}) {
+ return ListView.separated(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ padding: EdgeInsets.all(0.w),
+ itemCount: 4,
+ separatorBuilder: (_, __) => SizedBox(height: 12.h),
+ itemBuilder: (context, index) {
+ return Shimmer.fromColors(
+ baseColor: AppColors.shimmerBaseColor,
+ highlightColor: AppColors.shimmerHighlightColor,
+ child: Container(
+ height: isForHistory ? 60.h : 40.h,
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: BorderRadius.circular(10.r),
+ ),
+ ),
+ );
+ },
+ );
+ }
+
+ Widget _buildEmptyStateWidget() {
+ return SizedBox(
+ height: MediaQuery.of(context).size.height * 0.5,
+ child: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Utils.buildSvgWithAssets(
+ icon: AppAssets.calendar,
+ iconColor: AppColors.textColor,
+ height: 48.w,
+ width: 48.w,
+ ),
+ SizedBox(height: 16.h),
+ "You do not have any data available.".toText14(
+ weight: FontWeight.w500,
+ color: AppColors.textColor,
+ isCenter: true,
+ ),
+ SizedBox(height: 8.h),
+ _getEmptyStateMessage().toText12(
+ color: AppColors.greyTextColor,
+ isCenter: true,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildHistoryGraph() {
+ return Consumer(
+ builder: (context, viewModel, _) {
+ final selectedDuration = viewModel.selectedDurationFilter;
+ List dataPoints = [];
+ List? secondaryDataPoints;
+
+ switch (widget.trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ dataPoints = _buildBloodSugarGraphData(viewModel, selectedDuration);
+ break;
+ case HealthTrackerTypeEnum.bloodPressure:
+ final (systolicData, diastolicData) = _buildBloodPressureGraphData(viewModel, selectedDuration);
+ dataPoints = systolicData; // Systolic (primary line)
+ secondaryDataPoints = diastolicData; // Diastolic (secondary line)
+ break;
+ case HealthTrackerTypeEnum.weightTracker:
+ dataPoints = _buildWeightGraphData(viewModel, selectedDuration);
+ break;
+ }
+
+ if (dataPoints.isEmpty) {
+ return _buildEmptyStateWidget();
+ }
+
+ if (viewModel.isLoading) {
+ return Container(
+ padding: EdgeInsets.symmetric(vertical: 40.h),
+ child: _buildLoadingShimmer(),
+ );
+ }
+
+ // Calculate max value from both lines for blood pressure
+ double maxDataValue = dataPoints.isNotEmpty ? dataPoints.map((p) => p.value).reduce((a, b) => a > b ? a : b) : 0.0;
+ if (secondaryDataPoints != null && secondaryDataPoints.isNotEmpty) {
+ final secondaryMax = secondaryDataPoints.map((p) => p.value).reduce((a, b) => a > b ? a : b);
+ if (secondaryMax > maxDataValue) maxDataValue = secondaryMax;
+ }
+
+ double maxY = maxDataValue > 200 ? (maxDataValue * 1.2) : 250;
+ double minY = 0;
+ double horizontalInterval = maxY / 4;
+ double leftLabelInterval = horizontalInterval;
+
+ // Set colors based on tracker type
+ Color graphColor = AppColors.successColor;
+ Color? secondaryGraphColor;
+
+ if (widget.trackerType == HealthTrackerTypeEnum.bloodPressure) {
+ graphColor = AppColors.errorColor; // Red for Systolic
+ secondaryGraphColor = AppColors.blueColor; // Blue for Diastolic
+ }
+
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Legend for blood pressure
+ if (widget.trackerType == HealthTrackerTypeEnum.bloodPressure) ...[
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ _buildLegendItem(AppColors.errorColor, "Systolic".needTranslation),
+ SizedBox(width: 24.w),
+ _buildLegendItem(AppColors.blueColor, "Diastolic".needTranslation),
+ ],
+ ),
+ SizedBox(height: 12.h),
+ ],
+ CustomGraph(
+ bottomLabelReservedSize: 30,
+ dataPoints: dataPoints,
+ secondaryDataPoints: secondaryDataPoints,
+ makeGraphBasedOnActualValue: false,
+ leftLabelReservedSize: 50.h,
+ showGridLines: true,
+ maxY: maxY,
+ minY: minY,
+ showLinePoints: true,
+ maxX: dataPoints.length > 1 ? dataPoints.length.toDouble() - 0.75 : 1.0,
+ horizontalInterval: horizontalInterval,
+ leftLabelInterval: leftLabelInterval,
+ showShadow: widget.trackerType != HealthTrackerTypeEnum.bloodPressure,
+ graphColor: graphColor,
+ secondaryGraphColor: secondaryGraphColor,
+ graphShadowColor: graphColor.withValues(alpha: 0.15),
+ getDrawingHorizontalLine: (value) {
+ if (value % horizontalInterval == 0 && value > 0) {
+ return FlLine(
+ color: AppColors.greyTextColor.withValues(alpha: 0.3),
+ strokeWidth: 1.5,
+ dashArray: [8, 4],
+ );
+ }
+ return FlLine(color: AppColors.transparent, strokeWidth: 0);
+ },
+ leftLabelFormatter: (value) {
+ final interval = maxY / 4;
+ final positions = [0.0, interval, interval * 2, interval * 3, maxY];
+ for (var position in positions) {
+ if ((value - position).abs() < 1) {
+ return '${value.toInt()}'.toText10(weight: FontWeight.w600);
+ }
+ }
+ return SizedBox.shrink();
+ },
+ bottomLabelFormatter: (value, data) {
+ if (data.isEmpty) return SizedBox.shrink();
+ if ((value - value.round()).abs() > 0.01) return SizedBox.shrink();
+ int index = value.round();
+ if (index < 0 || index >= data.length) return SizedBox.shrink();
+
+ if (selectedDuration == 'Week' && index < 7) {
+ return Padding(
+ padding: EdgeInsets.only(top: 10.h),
+ child: data[index].label.toText10(weight: FontWeight.w600, color: AppColors.labelTextColor),
+ );
+ }
+ if (selectedDuration == 'Month' && index < 6) {
+ return Padding(
+ padding: EdgeInsets.only(top: 10.h),
+ child: data[index].label.toText10(weight: FontWeight.w600, color: AppColors.labelTextColor),
+ );
+ }
+ if (selectedDuration == 'Year' && index < 12) {
+ return Padding(
+ padding: EdgeInsets.only(top: 10.h),
+ child: data[index].label.toText8(fontWeight: FontWeight.w600, color: AppColors.labelTextColor),
+ );
+ }
+ return SizedBox.shrink();
+ },
+ scrollDirection: selectedDuration == 'Year' ? Axis.horizontal : Axis.vertical,
+ height: 250.h,
+ spotColor: graphColor,
+ ),
+ ],
+ );
+ },
+ );
+ }
+
+ /// Build legend item for graph
+ Widget _buildLegendItem(Color color, String label) {
+ return Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Container(
+ width: 12.w,
+ height: 12.w,
+ decoration: BoxDecoration(
+ color: color,
+ borderRadius: BorderRadius.circular(2.r),
+ ),
+ ),
+ SizedBox(width: 6.w),
+ label.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
+ ],
+ );
+ }
+
+ /// Build graph data for Blood Sugar
+ List _buildBloodSugarGraphData(HealthTrackersViewModel viewModel, String selectedDuration) {
+ List dataPoints = [];
+ final unit = _getUnit();
+
+ if (selectedDuration == 'Week') {
+ final weekResults = viewModel.weekDiabeticResultAverage;
+ if (weekResults.isNotEmpty) {
+ final sortedResults = List.from(weekResults);
+ sortedResults.sort((a, b) => (a.dateChart ?? DateTime.now()).compareTo(b.dateChart ?? DateTime.now()));
+ final last7Days = sortedResults.length > 7 ? sortedResults.sublist(sortedResults.length - 7) : sortedResults;
+
+ for (var result in last7Days) {
+ final value = result.dailyAverageResult?.toDouble() ?? 0.0;
+ final date = result.dateChart ?? DateTime.now();
+ final label = _getDayName(date).substring(0, 3);
+ dataPoints.add(DataPoint(
+ value: value,
+ label: label,
+ actualValue: value.toStringAsFixed(1),
+ time: date,
+ displayTime: _getDayName(date),
+ unitOfMeasurement: unit));
+ }
+ }
+ } else if (selectedDuration == 'Month') {
+ final monthResults = viewModel.monthDiabeticResultAverage;
+ if (monthResults.isNotEmpty) {
+ for (int i = 0; i < monthResults.length; i++) {
+ final weekData = monthResults[i];
+ final value = (weekData.weekAverageResult is num)
+ ? (weekData.weekAverageResult as num).toDouble()
+ : double.tryParse(weekData.weekAverageResult?.toString() ?? '0') ?? 0.0;
+ final weekLabel = weekData.weekDesc ?? 'Week ${i + 1}';
+ dataPoints.add(DataPoint(
+ value: value,
+ label: 'W${i + 1}',
+ actualValue: value.toStringAsFixed(1),
+ time: DateTime.now(),
+ displayTime: weekLabel,
+ unitOfMeasurement: unit));
+ }
+ }
+ } else if (selectedDuration == 'Year') {
+ final yearResults = viewModel.yearDiabeticResultAverage;
+ if (yearResults.isNotEmpty) {
+ for (int targetMonth = 1; targetMonth <= 12; targetMonth++) {
+ final monthData = yearResults.firstWhere((m) => m.monthNumber == targetMonth,
+ orElse: () => YearDiabeticResultAverage(monthAverageResult: 0.0, monthNumber: targetMonth, monthName: _getMonthName(targetMonth)));
+ final value = monthData.monthAverageResult?.toDouble() ?? 0.0;
+ final monthName = monthData.monthName ?? _getMonthName(targetMonth);
+ final label = monthName.length >= 3 ? monthName.substring(0, 3) : monthName;
+ dataPoints.add(DataPoint(
+ value: value,
+ label: label,
+ actualValue: value.toStringAsFixed(1),
+ time: DateTime(DateTime.now().year, targetMonth, 1),
+ displayTime: monthName,
+ unitOfMeasurement: unit));
+ }
+ }
+ }
+ return dataPoints;
+ }
+
+ /// Build graph data for Blood Pressure - returns (systolicData, diastolicData)
+ (List, List) _buildBloodPressureGraphData(HealthTrackersViewModel viewModel, String selectedDuration) {
+ List systolicDataPoints = [];
+ List diastolicDataPoints = [];
+ const unit = 'mmHg';
+
+ if (selectedDuration == 'Week') {
+ final weekResults = viewModel.weekBloodPressureResult;
+ if (weekResults.isNotEmpty) {
+ final sortedResults = List.from(weekResults);
+ sortedResults.sort((a, b) => (a.bloodPressureDate ?? DateTime.now()).compareTo(b.bloodPressureDate ?? DateTime.now()));
+ final last7Days = sortedResults.length > 7 ? sortedResults.sublist(sortedResults.length - 7) : sortedResults;
+
+ for (var result in last7Days) {
+ final systolic = (result.systolicePressure ?? 0).toDouble();
+ final diastolic = (result.diastolicPressure ?? 0).toDouble();
+ final date = result.bloodPressureDate ?? DateTime.now();
+ final label = _getDayName(date).substring(0, 3);
+
+ systolicDataPoints.add(DataPoint(
+ value: systolic,
+ label: label,
+ actualValue: '${systolic.toInt()}/${diastolic.toInt()}',
+ time: date,
+ displayTime: _getDayName(date),
+ unitOfMeasurement: unit));
+
+ diastolicDataPoints.add(DataPoint(
+ value: diastolic,
+ label: label,
+ actualValue: diastolic.toStringAsFixed(0),
+ time: date,
+ displayTime: _getDayName(date),
+ unitOfMeasurement: unit));
+ }
+ }
+ } else if (selectedDuration == 'Month') {
+ final monthResults = viewModel.monthBloodPressureResult;
+ if (monthResults.isNotEmpty) {
+ // Group by week and calculate averages
+ final Map> weekGroups = {};
+ for (var result in monthResults) {
+ final weekDesc = result.weekDesc ?? 'Week 1';
+ weekGroups.putIfAbsent(weekDesc, () => []);
+ weekGroups[weekDesc]!.add(result);
+ }
+
+ int weekIndex = 0;
+ for (var entry in weekGroups.entries) {
+ final weekData = entry.value;
+ double avgSystolic = 0;
+ double avgDiastolic = 0;
+
+ for (var result in weekData) {
+ avgSystolic += (result.systolicePressure ?? 0);
+ avgDiastolic += (result.diastolicPressure ?? 0);
+ }
+
+ if (weekData.isNotEmpty) {
+ avgSystolic = avgSystolic / weekData.length;
+ avgDiastolic = avgDiastolic / weekData.length;
+ }
+
+ final weekLabel = entry.key;
+ systolicDataPoints.add(DataPoint(
+ value: avgSystolic,
+ label: 'W${weekIndex + 1}',
+ actualValue: '${avgSystolic.toInt()}/${avgDiastolic.toInt()}',
+ time: DateTime.now(),
+ displayTime: weekLabel,
+ unitOfMeasurement: unit));
+
+ diastolicDataPoints.add(DataPoint(
+ value: avgDiastolic,
+ label: 'W${weekIndex + 1}',
+ actualValue: avgDiastolic.toStringAsFixed(0),
+ time: DateTime.now(),
+ displayTime: weekLabel,
+ unitOfMeasurement: unit));
+
+ weekIndex++;
+ }
+ }
+ } else if (selectedDuration == 'Year') {
+ final yearResults = viewModel.yearBloodPressureResult;
+ if (yearResults.isNotEmpty) {
+ // Group by month and calculate averages
+ final Map> monthGroups = {};
+ for (var result in yearResults) {
+ final chartMonth = result.chartMonth;
+ final monthNum = _getMonthNumber(chartMonth ?? 'January');
+ monthGroups.putIfAbsent(monthNum, () => []);
+ monthGroups[monthNum]!.add(result);
+ }
+
+ for (int targetMonth = 1; targetMonth <= 12; targetMonth++) {
+ double avgSystolic = 0;
+ double avgDiastolic = 0;
+
+ if (monthGroups.containsKey(targetMonth) && monthGroups[targetMonth]!.isNotEmpty) {
+ final monthData = monthGroups[targetMonth]!;
+ for (var result in monthData) {
+ avgSystolic += (result.systolicePressure ?? 0);
+ avgDiastolic += (result.diastolicPressure ?? 0);
+ }
+ avgSystolic = avgSystolic / monthData.length;
+ avgDiastolic = avgDiastolic / monthData.length;
+ }
+
+ final monthName = _getMonthName(targetMonth);
+ final label = monthName.length >= 3 ? monthName.substring(0, 3) : monthName;
+
+ systolicDataPoints.add(DataPoint(
+ value: avgSystolic,
+ label: label,
+ actualValue: '${avgSystolic.toInt()}/${avgDiastolic.toInt()}',
+ time: DateTime(DateTime.now().year, targetMonth, 1),
+ displayTime: monthName,
+ unitOfMeasurement: unit));
+
+ diastolicDataPoints.add(DataPoint(
+ value: avgDiastolic,
+ label: label,
+ actualValue: avgDiastolic.toStringAsFixed(0),
+ time: DateTime(DateTime.now().year, targetMonth, 1),
+ displayTime: monthName,
+ unitOfMeasurement: unit));
+ }
+ }
+ }
+ return (systolicDataPoints, diastolicDataPoints);
+ }
+
+ /// Helper to get month number from name
+ int _getMonthNumber(String monthName) {
+ const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+ final index = months.indexWhere((m) => m.toLowerCase() == monthName.toLowerCase());
+ return index >= 0 ? index + 1 : 1;
+ }
+
+ /// Build graph data for Weight
+ List _buildWeightGraphData(HealthTrackersViewModel viewModel, String selectedDuration) {
+ List dataPoints = [];
+ final unit = _getUnit();
+
+ if (selectedDuration == 'Week') {
+ final weekResults = viewModel.weekWeightMeasurementResultAverage;
+ if (weekResults.isNotEmpty) {
+ final sortedResults = List.from(weekResults);
+ sortedResults.sort((a, b) => (a.weightDate ?? DateTime.now()).compareTo(b.weightDate ?? DateTime.now()));
+ final last7Days = sortedResults.length > 7 ? sortedResults.sublist(sortedResults.length - 7) : sortedResults;
+
+ for (var result in last7Days) {
+ final value = result.dailyAverageResult?.toDouble() ?? 0.0;
+ final date = result.weightDate ?? DateTime.now();
+ final label = _getDayName(date).substring(0, 3);
+ dataPoints.add(DataPoint(
+ value: value,
+ label: label,
+ actualValue: value.toStringAsFixed(1),
+ time: date,
+ displayTime: _getDayName(date),
+ unitOfMeasurement: unit));
+ }
+ }
+ } else if (selectedDuration == 'Month') {
+ final monthResults = viewModel.monthWeightMeasurementResultAverage;
+ if (monthResults.isNotEmpty) {
+ for (int i = 0; i < monthResults.length; i++) {
+ final weekData = monthResults[i];
+ final value = weekData.weekAverageResult?.toDouble() ?? 0.0;
+ final weekLabel = weekData.weekDesc ?? 'Week ${i + 1}';
+ dataPoints.add(DataPoint(
+ value: value,
+ label: 'W${i + 1}',
+ actualValue: value.toStringAsFixed(1),
+ time: DateTime.now(),
+ displayTime: weekLabel,
+ unitOfMeasurement: unit));
+ }
+ }
+ } else if (selectedDuration == 'Year') {
+ final yearResults = viewModel.yearWeightMeasurementResultAverage;
+ if (yearResults.isNotEmpty) {
+ for (int targetMonth = 1; targetMonth <= 12; targetMonth++) {
+ final monthData = yearResults.firstWhere((m) => m.monthNumber == targetMonth,
+ orElse: () =>
+ YearWeightMeasurementResultAverage(monthAverageResult: 0.0, monthNumber: targetMonth, monthName: _getMonthName(targetMonth)));
+ final value = monthData.monthAverageResult?.toDouble() ?? 0.0;
+ final monthName = monthData.monthName ?? _getMonthName(targetMonth);
+ final label = monthName.length >= 3 ? monthName.substring(0, 3) : monthName;
+ dataPoints.add(DataPoint(
+ value: value,
+ label: label,
+ actualValue: value.toStringAsFixed(1),
+ time: DateTime(DateTime.now().year, targetMonth, 1),
+ displayTime: monthName,
+ unitOfMeasurement: unit));
+ }
+ }
+ }
+ return dataPoints;
+ }
+
+ void onSendEmailPressed(BuildContext context) async {
+ // TODO: Implement send email functionality
+ }
+
+ Widget _buildPageShimmer() {
+ return Container(
+ margin: EdgeInsets.symmetric(horizontal: 24.w),
+ child: Column(
+ children: [
+ SizedBox(height: 16.h),
+ Shimmer.fromColors(
+ baseColor: AppColors.shimmerBaseColor,
+ highlightColor: AppColors.shimmerHighlightColor,
+ child: Container(
+ height: 120.h,
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: BorderRadius.circular(24.r),
+ ),
+ ),
+ ),
+ SizedBox(height: 16.h),
+ Shimmer.fromColors(
+ baseColor: AppColors.shimmerBaseColor,
+ highlightColor: AppColors.shimmerHighlightColor,
+ child: Container(
+ height: 300.h,
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: BorderRadius.circular(24.r),
+ ),
+ ),
+ ),
+ SizedBox(height: 16.h),
+ ],
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: CollapsingListView(
+ sendEmail: () async => onSendEmailPressed(context),
+ title: _getPageTitle(),
+ bottomChild: Consumer(
+ builder: (context, viewModel, child) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(24.w),
+ child: CustomButton(
+ text: "Add new Record".needTranslation,
+ onPressed: () {
+ if (!viewModel.isLoading) {
+ context.navigateWithName(AppRoutes.addHealthTrackerEntryPage, arguments: widget.trackerType);
+ }
+ },
+ icon: AppAssets.add_icon,
+ borderRadius: 12.r,
+ borderColor: AppColors.transparent,
+ padding: EdgeInsets.symmetric(vertical: 14.h),
+ ),
+ ),
+ );
+ },
+ ),
+ child: Consumer(
+ builder: (context, viewModel, child) {
+ if (viewModel.isLoading) {
+ return _buildPageShimmer();
+ }
+
+ if (_hasNoData(viewModel)) {
+ return _buildEmptyStateWidget();
+ }
+
+ return Column(
+ children: [
+ SizedBox(height: 16.h),
+ TrackerLastValueCard(trackerType: widget.trackerType),
+ SizedBox(height: 16.h),
+ _buildHistoryGraphOrList(),
+ SizedBox(height: 16.h),
+ ],
+ );
+ },
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/health_trackers/health_trackers_page.dart b/lib/presentation/health_trackers/health_trackers_page.dart
new file mode 100644
index 0000000..c55e8b4
--- /dev/null
+++ b/lib/presentation/health_trackers/health_trackers_page.dart
@@ -0,0 +1,118 @@
+import 'package:flutter/material.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/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/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+
+class HealthTrackersPage extends StatefulWidget {
+ const HealthTrackersPage({super.key});
+
+ @override
+ State createState() => _HealthTrackersPageState();
+}
+
+Widget buildHealthTrackerCard({
+ required String icon,
+ required String title,
+ required String description,
+ required Color iconBgColor,
+ required VoidCallback onTap,
+}) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 20.r),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: iconBgColor, borderRadius: 10.r),
+ height: 40.w,
+ width: 40.w,
+ child: Utils.buildSvgWithAssets(
+ icon: icon,
+ fit: BoxFit.none,
+ height: 22.w,
+ width: 22.w,
+ ),
+ ),
+ SizedBox(width: 12.w),
+ Flexible(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ title.toText16(weight: FontWeight.w600),
+ description.toText12(
+ fontWeight: FontWeight.w500,
+ color: Color(0xFF8F9AA3),
+ ),
+ ],
+ ),
+ ),
+ SizedBox(width: 12.w),
+ Utils.buildSvgWithAssets(
+ icon: AppAssets.arrowRight,
+ width: 24.w,
+ height: 24.h,
+ fit: BoxFit.contain,
+ iconColor: AppColors.textColor,
+ ),
+ ],
+ ).paddingAll(16.w),
+ ).onPress(onTap);
+}
+
+class _HealthTrackersPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return CollapsingListView(
+ title: "Health Trackers".needTranslation,
+ child: Column(
+ children: [
+ buildHealthTrackerCard(
+ iconBgColor: AppColors.primaryRedColor,
+ icon: AppAssets.bloodSugarOnlyIcon,
+ title: "Blood Sugar".needTranslation,
+ description: "Track your glucose levels, understand trends, and get personalized insights for better health.".needTranslation,
+ onTap: () {
+ context.navigateWithName(
+ AppRoutes.healthTrackerDetailPage,
+ arguments: HealthTrackerTypeEnum.bloodSugar,
+ );
+ },
+ ),
+ SizedBox(height: 16.h),
+ buildHealthTrackerCard(
+ iconBgColor: AppColors.infoColor,
+ icon: AppAssets.bloodPressureIcon,
+ title: "Blood Pressure".needTranslation,
+ description: "Monitor your blood pressure levels, track systolic and diastolic readings, and maintain a healthy heart.".needTranslation,
+ onTap: () {
+ context.navigateWithName(
+ AppRoutes.healthTrackerDetailPage,
+ arguments: HealthTrackerTypeEnum.bloodPressure,
+ );
+ },
+ ),
+ SizedBox(height: 16.h),
+ buildHealthTrackerCard(
+ iconBgColor: AppColors.successColor,
+ icon: AppAssets.weightIcon,
+ title: "Weight".needTranslation,
+ description: "Track your weight progress, set goals, and maintain a healthy body mass for overall wellness.".needTranslation,
+ onTap: () {
+ context.navigateWithName(
+ AppRoutes.healthTrackerDetailPage,
+ arguments: HealthTrackerTypeEnum.weightTracker,
+ );
+ },
+ ),
+ ],
+ ).paddingSymmetrical(20.w, 24.h),
+ );
+ }
+}
diff --git a/lib/presentation/health_trackers/health_trackers_view_model.dart b/lib/presentation/health_trackers/health_trackers_view_model.dart
new file mode 100644
index 0000000..059c11e
--- /dev/null
+++ b/lib/presentation/health_trackers/health_trackers_view_model.dart
@@ -0,0 +1,1012 @@
+import 'dart:developer';
+
+import 'package:flutter/cupertino.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_repo.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/blood_pressure/blood_pressure_result.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/blood_pressure/month_blood_pressure_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/blood_pressure/week_blood_pressure_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/blood_pressure/year_blood_pressure_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/blood_sugar/diabetic_patient_result.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/blood_sugar/month_diabetic_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/blood_sugar/week_diabetic_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/blood_sugar/year_diabetic_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/weight/month_weight_measurement_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/weight/week_weight_measurement_result_average.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/weight/weight_measurement_result.dart';
+import 'package:hmg_patient_app_new/features/health_trackers/models/weight/year_weight_measurement_result_average.dart';
+import 'package:hmg_patient_app_new/services/error_handler_service.dart';
+
+class HealthTrackersViewModel extends ChangeNotifier {
+ HealthTrackersRepo healthTrackersRepo;
+ ErrorHandlerService errorHandlerService;
+
+ HealthTrackersViewModel({required this.healthTrackersRepo, required this.errorHandlerService});
+
+ // ==================== STATE MANAGEMENT ====================
+ bool isLoading = false;
+ String? _errorMessage;
+
+ String? get errorMessage => _errorMessage;
+
+ List get durationFilters => ["Week", "Month", "Year"];
+
+ String _selectedDuration = "Week";
+ bool _isGraphView = true;
+
+ String get selectedDurationFilter => _selectedDuration;
+
+ bool get isGraphView => _isGraphView;
+
+ final List bloodSugarUnit = ['mg/dlt', 'mol/L'];
+
+ String _selectedBloodSugarUnit = 'mg/dlt';
+
+ String get selectedBloodSugarUnit => _selectedBloodSugarUnit;
+
+ String _selectedBloodSugarMeasureTime = '';
+
+ String get selectedBloodSugarMeasureTime => _selectedBloodSugarMeasureTime;
+
+ final List bloodSugarMeasureTimeEnList = [
+ 'Before Breakfast',
+ 'After Breakfast',
+ 'Before Lunch',
+ 'After Lunch',
+ 'Before Dinner',
+ 'After Dinner',
+ 'Before Sleep',
+ 'After Sleep',
+ 'Fasting',
+ 'Other',
+ ];
+ final List bloodSugarMeasureTimeArList = [
+ "قبل الإفطار",
+ "بعد الإفطار",
+ "قبل الغداء",
+ "بعد الغداء",
+ "قبل العشاء",
+ "بعد العشاء",
+ "قبل النوم",
+ "بعد النوم",
+ "صائم",
+ "آخر",
+ ];
+
+ // Setters with notification
+ void setBloodSugarMeasureTime(String duration) async {
+ _selectedBloodSugarMeasureTime = duration;
+ notifyListeners();
+ }
+
+ // Setters with notification
+ void setFilterDuration(String duration) async {
+ _selectedDuration = duration;
+ notifyListeners();
+ }
+
+ // Setters with notification
+ void setGraphView(bool value) {
+ _isGraphView = value;
+ notifyListeners();
+ }
+
+ void setBloodSugarUnit(String unit) {
+ _selectedBloodSugarUnit = unit;
+ notifyListeners();
+ }
+
+ // ==================== WEIGHT FORM FIELDS ====================
+ final List weightUnits = ['kg', 'lb'];
+ String _selectedWeightUnit = 'kg';
+
+ String get selectedWeightUnit => _selectedWeightUnit;
+
+ void setWeightUnit(String unit) {
+ _selectedWeightUnit = unit;
+ notifyListeners();
+ }
+
+ // ==================== BLOOD PRESSURE FORM FIELDS ====================
+ final List measuredArmList = ['Left Arm', 'Right Arm'];
+ String _selectedMeasuredArm = '';
+
+ String get selectedMeasuredArm => _selectedMeasuredArm;
+
+ void setMeasuredArm(String arm) {
+ _selectedMeasuredArm = arm;
+ notifyListeners();
+ }
+
+ // Text Controllers
+ TextEditingController weightController = TextEditingController();
+ TextEditingController bloodSugarController = TextEditingController();
+ TextEditingController systolicController = TextEditingController();
+ TextEditingController diastolicController = TextEditingController();
+
+ // Get current progress list based on selected duration
+ // dynamic get currentProgressData {
+ // switch (_selectedDuration) {
+ // case 'Daily':
+ // return _todayProgressList;
+ // case 'Weekly':
+ // return _weekProgressList;
+ // case 'Monthly':
+ // return _monthProgressList;
+ // default:
+ // return _todayProgressList;
+ // }
+ // }
+
+ // ==================== WEIGHT TRACKING DATA ====================
+ final List _monthWeightMeasurementResultAverage = [];
+ final List _weekWeightMeasurementResultAverage = [];
+ final List _yearWeightMeasurementResultAverage = [];
+
+ final List _monthWeightMeasurementResult = [];
+ final List _weekWeightMeasurementResult = [];
+ final List _yearWeightMeasurementResult = [];
+
+ // Getters for weight data
+ List get monthWeightMeasurementResultAverage => _monthWeightMeasurementResultAverage;
+
+ List get weekWeightMeasurementResultAverage => _weekWeightMeasurementResultAverage;
+
+ List get yearWeightMeasurementResultAverage => _yearWeightMeasurementResultAverage;
+
+ List get monthWeightMeasurementResult => _monthWeightMeasurementResult;
+
+ List get weekWeightMeasurementResult => _weekWeightMeasurementResult;
+
+ List get yearWeightMeasurementResult => _yearWeightMeasurementResult;
+
+ // ==================== BLOOD PRESSURE TRACKING DATA ====================
+ final List _monthBloodPressureResultAverage = [];
+ final List _weekBloodPressureResultAverage = [];
+ final List _yearBloodPressureResultAverage = [];
+
+ final List _monthBloodPressureResult = [];
+ final List _weekBloodPressureResult = [];
+ final List _yearBloodPressureResult = [];
+
+ // Getters for blood pressure data
+ List get monthBloodPressureResultAverage => _monthBloodPressureResultAverage;
+
+ List get weekBloodPressureResultAverage => _weekBloodPressureResultAverage;
+
+ List get yearBloodPressureResultAverage => _yearBloodPressureResultAverage;
+
+ List get monthBloodPressureResult => _monthBloodPressureResult;
+
+ List get weekBloodPressureResult => _weekBloodPressureResult;
+
+ List get yearBloodPressureResult => _yearBloodPressureResult;
+
+ // ==================== BLOOD SUGAR (DIABETIC) TRACKING DATA ====================
+ final List _monthDiabeticResultAverage = [];
+ final List _weekDiabeticResultAverage = [];
+ final List _yearDiabeticResultAverage = [];
+
+ final List _monthDiabeticPatientResult = [];
+ final List _weekDiabeticPatientResult = [];
+ final List _yearDiabeticPatientResult = [];
+
+ // Getters for blood sugar data
+ List get monthDiabeticResultAverage => _monthDiabeticResultAverage;
+
+ List get weekDiabeticResultAverage => _weekDiabeticResultAverage;
+
+ List get yearDiabeticResultAverage => _yearDiabeticResultAverage;
+
+ List get monthDiabeticPatientResult => _monthDiabeticPatientResult;
+
+ List get weekDiabeticPatientResult => _weekDiabeticPatientResult;
+
+ List get yearDiabeticPatientResult => _yearDiabeticPatientResult;
+
+ // ==================== WEIGHT TRACKING METHODS ====================
+
+ /// Fetch weight averages and results
+ Future getWeight() async {
+ isLoading = true;
+ notifyListeners();
+
+ try {
+ // Fetch weight averages
+ final averageResult = await healthTrackersRepo.getWeightMeasurementResultAverage();
+
+ averageResult.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) {
+ final data = apiModel.data;
+ if (data is Map) {
+ // Clear existing data
+ _monthWeightMeasurementResultAverage.clear();
+ _weekWeightMeasurementResultAverage.clear();
+ _yearWeightMeasurementResultAverage.clear();
+
+ // Parse month averages
+ if (data['monthAverageList'] != null) {
+ for (var item in (data['monthAverageList'] as List)) {
+ _monthWeightMeasurementResultAverage.add(
+ MonthWeightMeasurementResultAverage.fromJson(item),
+ );
+ }
+ }
+
+ // Parse week averages
+ if (data['weekAverageList'] != null) {
+ for (var item in (data['weekAverageList'] as List)) {
+ _weekWeightMeasurementResultAverage.add(
+ WeekWeightMeasurementResultAverage.fromJson(item),
+ );
+ }
+ }
+
+ // Parse year averages
+ if (data['yearAverageList'] != null) {
+ for (var item in (data['yearAverageList'] as List)) {
+ _yearWeightMeasurementResultAverage.add(
+ YearWeightMeasurementResultAverage.fromJson(item),
+ );
+ }
+ }
+ }
+ },
+ );
+
+ // Fetch weight results
+ final resultsResponse = await healthTrackersRepo.getWeightMeasurementResults();
+
+ resultsResponse.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) {
+ final data = apiModel.data;
+ if (data is Map) {
+ // Clear existing data
+ _monthWeightMeasurementResult.clear();
+ _weekWeightMeasurementResult.clear();
+ _yearWeightMeasurementResult.clear();
+
+ // Parse week results
+ if (data['weekResultList'] != null) {
+ for (var item in (data['weekResultList'] as List)) {
+ _weekWeightMeasurementResult.add(WeightMeasurementResult.fromJson(item));
+ }
+ }
+
+ // Parse month results
+ if (data['monthResultList'] != null) {
+ for (var item in (data['monthResultList'] as List)) {
+ _monthWeightMeasurementResult.add(WeightMeasurementResult.fromJson(item));
+ }
+ }
+
+ // Parse year results
+ if (data['yearResultList'] != null) {
+ for (var item in (data['yearResultList'] as List)) {
+ _yearWeightMeasurementResult.add(WeightMeasurementResult.fromJson(item));
+ }
+ }
+ }
+ },
+ );
+ } catch (e) {
+ log('Error in getWeight: $e');
+ } finally {
+ isLoading = false;
+ notifyListeners();
+ }
+ }
+
+ /// Add new weight result
+ Future addWeightResult({
+ required String weightDate,
+ required String weightMeasured,
+ required int weightUnit,
+ }) async {
+ try {
+ final result = await healthTrackersRepo.addWeightMeasurementResult(
+ weightDate: weightDate,
+ weightMeasured: weightMeasured,
+ weightUnit: weightUnit,
+ );
+
+ bool success = false;
+
+ result.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) async {
+ success = true;
+ // Refresh data after successful add
+ await getWeight();
+ },
+ );
+
+ return success;
+ } catch (e) {
+ log('Error in addWeightResult: $e');
+ return false;
+ }
+ }
+
+ /// Update existing weight result
+ Future updateWeightResult({
+ required int lineItemNo,
+ required int weightUnit,
+ required String weightMeasured,
+ required String weightDate,
+ }) async {
+ try {
+ final result = await healthTrackersRepo.updateWeightMeasurementResult(
+ lineItemNo: lineItemNo,
+ weightUnit: weightUnit,
+ weightMeasured: weightMeasured,
+ weightDate: weightDate,
+ );
+
+ bool success = false;
+
+ result.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) async {
+ success = true;
+ // Refresh data after successful update
+ await getWeight();
+ },
+ );
+
+ return success;
+ } catch (e) {
+ log('Error in updateWeightResult: $e');
+ return false;
+ }
+ }
+
+ /// Delete weight result
+ Future deleteWeightResult({
+ required int lineItemNo,
+ }) async {
+ try {
+ final result = await healthTrackersRepo.deactivateWeightMeasurementStatus(
+ lineItemNo: lineItemNo,
+ );
+
+ bool success = false;
+
+ result.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) async {
+ success = true;
+ // Refresh data after successful delete
+ await getWeight();
+ },
+ );
+
+ return success;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ // ==================== BLOOD PRESSURE TRACKING METHODS ====================
+
+ /// Fetch blood pressure averages and results
+ Future getBloodPressure() async {
+ isLoading = true;
+ notifyListeners();
+
+ try {
+ // Fetch blood pressure averages
+ final averageResult = await healthTrackersRepo.getBloodPressureResultAverage();
+
+ averageResult.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) {
+ final data = apiModel.data;
+
+ if (data is Map) {
+ // Clear existing data
+ _monthBloodPressureResultAverage.clear();
+ _weekBloodPressureResultAverage.clear();
+ _yearBloodPressureResultAverage.clear();
+
+ // Parse month averages
+ if (data['monthList'] != null) {
+ for (var item in (data['monthList'] as List)) {
+ _monthBloodPressureResultAverage.add(
+ MonthBloodPressureResultAverage.fromJson(item),
+ );
+ }
+ }
+
+ // Parse week averages
+ if (data['weekList'] != null) {
+ for (var item in (data['weekList'] as List)) {
+ _weekBloodPressureResultAverage.add(
+ WeekBloodPressureResultAverage.fromJson(item),
+ );
+ }
+ }
+
+ // Parse year averages
+ if (data['yearList'] != null) {
+ for (var item in (data['yearList'] as List)) {
+ _yearBloodPressureResultAverage.add(
+ YearBloodPressureResultAverage.fromJson(item),
+ );
+ }
+ }
+ }
+ },
+ );
+
+ // Fetch blood pressure results
+ final resultsResponse = await healthTrackersRepo.getBloodPressureResults();
+
+ resultsResponse.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) {
+ final data = apiModel.data;
+ if (data is Map) {
+ // Clear existing data
+ _monthBloodPressureResult.clear();
+ _weekBloodPressureResult.clear();
+ _yearBloodPressureResult.clear();
+
+ // Parse week results
+ if (data['weekList'] != null) {
+ for (var item in (data['weekList'] as List)) {
+ _weekBloodPressureResult.add(BloodPressureResult.fromJson(item));
+ }
+ }
+
+ // Parse month results
+ if (data['monthList'] != null) {
+ for (var item in (data['monthList'] as List)) {
+ _monthBloodPressureResult.add(BloodPressureResult.fromJson(item));
+ }
+ }
+
+ // Parse year results
+ if (data['yearList'] != null) {
+ for (var item in (data['yearList'] as List)) {
+ _yearBloodPressureResult.add(BloodPressureResult.fromJson(item));
+ }
+ }
+ }
+ },
+ );
+ } catch (e) {
+ log('Error in getBloodPressure: $e');
+ } finally {
+ isLoading = false;
+ notifyListeners();
+ }
+ }
+
+ /// Add or Update blood pressure result
+ Future addOrUpdateBloodPressureResult({
+ required String bloodPressureDate,
+ required String diastolicPressure,
+ required String systolicePressure,
+ required int measuredArm,
+ int? lineItemNo,
+ bool isUpdate = false,
+ }) async {
+ try {
+ final result = isUpdate
+ ? await healthTrackersRepo.updateBloodPressureResult(
+ bloodPressureDate: bloodPressureDate,
+ diastolicPressure: diastolicPressure,
+ systolicePressure: systolicePressure,
+ measuredArm: measuredArm,
+ lineItemNo: lineItemNo!,
+ )
+ : await healthTrackersRepo.addBloodPressureResult(
+ bloodPressureDate: bloodPressureDate,
+ diastolicPressure: diastolicPressure,
+ systolicePressure: systolicePressure,
+ measuredArm: measuredArm,
+ );
+
+ bool success = false;
+
+ result.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) async {
+ success = true;
+ // Refresh data after successful add/update
+ await getBloodPressure();
+ },
+ );
+
+ return success;
+ } catch (e) {
+ log('Error in addOrUpdateBloodPressureResult: $e');
+ return false;
+ }
+ }
+
+ /// Delete blood pressure result
+ Future deleteBloodPressureResult({
+ required int lineItemNo,
+ }) async {
+ try {
+ final result = await healthTrackersRepo.deactivateBloodPressureStatus(
+ lineItemNo: lineItemNo,
+ );
+
+ bool success = false;
+
+ result.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) async {
+ success = true;
+ // Refresh data after successful delete
+ await getBloodPressure();
+ },
+ );
+
+ return success;
+ } catch (e) {
+ log('Error in deleteBloodPressureResult: $e');
+ return false;
+ }
+ }
+
+ // ==================== BLOOD SUGAR (DIABETIC) TRACKING METHODS ====================
+
+ /// Fetch blood sugar averages and results
+ Future getBloodSugar() async {
+ isLoading = true;
+ notifyListeners();
+
+ try {
+ // Fetch blood sugar averages
+ final averageResult = await healthTrackersRepo.getDiabeticResultAverage();
+
+ averageResult.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) {
+ final data = apiModel.data;
+ if (data is Map) {
+ // Clear existing data
+ _monthDiabeticResultAverage.clear();
+ _weekDiabeticResultAverage.clear();
+ _yearDiabeticResultAverage.clear();
+
+ // Parse month averages
+ if (data['monthAverageList'] != null) {
+ for (var item in (data['monthAverageList'] as List)) {
+ _monthDiabeticResultAverage.add(
+ MonthDiabeticResultAverage.fromJson(item),
+ );
+ }
+ }
+
+ // Parse week averages
+ if (data['weekAverageList'] != null) {
+ for (var item in (data['weekAverageList'] as List)) {
+ _weekDiabeticResultAverage.add(
+ WeekDiabeticResultAverage.fromJson(item),
+ );
+ }
+ }
+
+ // Parse year averages
+ if (data['yearAverageList'] != null) {
+ for (var item in (data['yearAverageList'] as List)) {
+ _yearDiabeticResultAverage.add(
+ YearDiabeticResultAverage.fromJson(item),
+ );
+ }
+ }
+ }
+ },
+ );
+
+ // Fetch blood sugar results
+ final resultsResponse = await healthTrackersRepo.getDiabeticResults();
+
+ resultsResponse.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) {
+ final data = apiModel.data;
+ if (data is Map) {
+ // Clear existing data
+ _monthDiabeticPatientResult.clear();
+ _weekDiabeticPatientResult.clear();
+ _yearDiabeticPatientResult.clear();
+
+ // Parse week results
+ if (data['weekResultList'] != null) {
+ for (var item in (data['weekResultList'] as List)) {
+ _weekDiabeticPatientResult.add(DiabeticPatientResult.fromJson(item));
+ }
+ }
+
+ // Parse month results
+ if (data['monthResultList'] != null) {
+ for (var item in (data['monthResultList'] as List)) {
+ _monthDiabeticPatientResult.add(DiabeticPatientResult.fromJson(item));
+ }
+ }
+
+ // Parse year results
+ if (data['yearResultList'] != null) {
+ for (var item in (data['yearResultList'] as List)) {
+ _yearDiabeticPatientResult.add(DiabeticPatientResult.fromJson(item));
+ }
+ }
+ }
+ },
+ );
+ } catch (e) {
+ log('Error in getBloodSugar: $e');
+ } finally {
+ isLoading = false;
+ notifyListeners();
+ }
+ }
+
+ /// Add new blood sugar result
+ Future addBloodSugarResult({
+ required String bloodSugarDateChart,
+ required String bloodSugarResult,
+ required String diabeticUnit,
+ required int measuredTime,
+ }) async {
+ try {
+ final result = await healthTrackersRepo.addDiabeticResult(
+ bloodSugarDateChart: bloodSugarDateChart,
+ bloodSugarResult: bloodSugarResult,
+ diabeticUnit: diabeticUnit,
+ measuredTime: measuredTime,
+ );
+
+ bool success = false;
+
+ result.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) async {
+ success = true;
+ // Refresh data after successful add
+ await getBloodSugar();
+ },
+ );
+
+ return success;
+ } catch (e) {
+ log('Error in addBloodSugarResult: $e');
+ return false;
+ }
+ }
+
+ /// Update existing blood sugar result
+ Future updateBloodSugarResult({
+ required DateTime month,
+ required DateTime hour,
+ required String bloodSugarResult,
+ required String diabeticUnit,
+ required int measuredTime,
+ required int lineItemNo,
+ }) async {
+ try {
+ final result = await healthTrackersRepo.updateDiabeticResult(
+ month: month,
+ hour: hour,
+ bloodSugarResult: bloodSugarResult,
+ diabeticUnit: diabeticUnit,
+ measuredTime: measuredTime,
+ lineItemNo: lineItemNo,
+ );
+
+ bool success = false;
+
+ result.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) async {
+ success = true;
+ // Refresh data after successful update
+ await getBloodSugar();
+ },
+ );
+
+ return success;
+ } catch (e) {
+ log('Error in updateBloodSugarResult: $e');
+ return false;
+ }
+ }
+
+ /// Delete blood sugar result
+ Future deleteBloodSugarResult({
+ required int lineItemNo,
+ }) async {
+ try {
+ final result = await healthTrackersRepo.deactivateDiabeticStatus(
+ lineItemNo: lineItemNo,
+ );
+
+ bool success = false;
+
+ result.fold(
+ (failure) => errorHandlerService.handleError(failure: failure),
+ (apiModel) async {
+ success = true;
+ // Refresh data after successful delete
+ await getBloodSugar();
+ },
+ );
+
+ return success;
+ } catch (e) {
+ log('Error in deleteBloodSugarResult: $e');
+ return false;
+ }
+ }
+
+ // Validation method
+ String? _validateBloodSugarEntry(String dateTime) {
+ // Validate blood sugar value
+ if (bloodSugarController.text.trim().isEmpty) {
+ return "Please enter blood sugar value";
+ }
+
+ final bloodSugarValue = double.tryParse(bloodSugarController.text.trim());
+ if (bloodSugarValue == null) {
+ return "Please enter a valid number";
+ }
+
+ if (bloodSugarValue <= 0) {
+ return "Blood sugar value must be greater than 0";
+ }
+
+ // Validate reasonable range (typical ranges)
+ if (bloodSugarValue > 1000) {
+ return "Blood sugar value seems too high. Please check and enter again";
+ }
+
+ // Validate date time
+ if (dateTime.trim().isEmpty) {
+ return "Please select date and time";
+ }
+
+ // Validate measure time
+ if (_selectedBloodSugarMeasureTime.isEmpty) {
+ return "Please select when the measurement was taken";
+ }
+
+ return null; // No errors
+ }
+
+ // Save blood sugar entry with validation
+ Future saveBloodSugarEntry({
+ required String dateTime,
+ required String measureTime,
+ Function()? onSuccess,
+ Function(String error)? onFailure,
+ }) async {
+ // Validate
+ final validationError = _validateBloodSugarEntry(dateTime);
+ if (validationError != null) {
+ _errorMessage = validationError;
+ if (onFailure != null) onFailure(validationError);
+ return;
+ }
+
+ // Clear previous error and show loading
+ _errorMessage = null;
+ isLoading = true;
+ notifyListeners();
+
+ try {
+ // Get measure time index (0-based, but API expects 1-based)
+ final measureTimeIndex = bloodSugarMeasureTimeEnList.indexOf(measureTime);
+
+ // Call API
+ final success = await addBloodSugarResult(
+ bloodSugarDateChart: dateTime,
+ bloodSugarResult: bloodSugarController.text.trim(),
+ diabeticUnit: _selectedBloodSugarUnit,
+ measuredTime: measureTimeIndex >= 0 ? measureTimeIndex : 0,
+ );
+
+ isLoading = false;
+
+ if (success) {
+ // Clear form after successful save
+ bloodSugarController.clear();
+ _selectedBloodSugarMeasureTime = '';
+ notifyListeners();
+ if (onSuccess != null) onSuccess();
+ } else {
+ _errorMessage = "Failed to save blood sugar entry. Please try again";
+ notifyListeners();
+ if (onFailure != null) onFailure(_errorMessage!);
+ }
+ } catch (e) {
+ log('Error in saveBloodSugarEntry: $e');
+ _errorMessage = "An error occurred. Please try again";
+ isLoading = false;
+ notifyListeners();
+ if (onFailure != null) onFailure(_errorMessage!);
+ }
+ }
+
+ // ==================== WEIGHT ENTRY METHODS ====================
+
+ // Validate weight entry before saving
+ String? _validateWeightEntry(String dateTime) {
+ // Validate weight value
+ final weightValue = weightController.text.trim();
+ if (weightValue.isEmpty) {
+ return "Please enter weight value";
+ }
+
+ // Check if it's a valid number
+ final parsedValue = double.tryParse(weightValue);
+ if (parsedValue == null || parsedValue <= 0) {
+ return "Please enter a valid weight value";
+ }
+
+ // Validate date time
+ if (dateTime.trim().isEmpty) {
+ return "Please select date and time";
+ }
+
+ return null; // No errors
+ }
+
+ // Save weight entry with validation
+ Future saveWeightEntry({
+ required String dateTime,
+ Function()? onSuccess,
+ Function(String error)? onFailure,
+ }) async {
+ // Validate
+ final validationError = _validateWeightEntry(dateTime);
+ if (validationError != null) {
+ _errorMessage = validationError;
+ if (onFailure != null) onFailure(validationError);
+ return;
+ }
+
+ // Clear previous error and show loading
+ _errorMessage = null;
+ isLoading = true;
+ notifyListeners();
+
+ try {
+ // Get weight unit index (0 = kg, 1 = lb)
+ final weightUnitIndex = weightUnits.indexOf(_selectedWeightUnit);
+
+ // Call API
+ final success = await addWeightResult(
+ weightDate: dateTime,
+ weightMeasured: weightController.text.trim(),
+ weightUnit: weightUnitIndex >= 0 ? weightUnitIndex : 0,
+ );
+
+ isLoading = false;
+
+ if (success) {
+ // Clear form after successful save
+ weightController.clear();
+ notifyListeners();
+ if (onSuccess != null) onSuccess();
+ } else {
+ _errorMessage = "Failed to save weight entry. Please try again";
+ notifyListeners();
+ if (onFailure != null) onFailure(_errorMessage!);
+ }
+ } catch (e) {
+ log('Error in saveWeightEntry: $e');
+ _errorMessage = "An error occurred. Please try again";
+ isLoading = false;
+ notifyListeners();
+ if (onFailure != null) onFailure(_errorMessage!);
+ }
+ }
+
+ // ==================== BLOOD PRESSURE ENTRY METHODS ====================
+
+ // Validate blood pressure entry before saving
+ String? _validateBloodPressureEntry(String dateTime) {
+ // Validate systolic value
+ final systolicValue = systolicController.text.trim();
+ if (systolicValue.isEmpty) {
+ return "Please enter systolic value";
+ }
+ final parsedSystolic = int.tryParse(systolicValue);
+ if (parsedSystolic == null || parsedSystolic <= 0) {
+ return "Please enter a valid systolic value";
+ }
+
+ // Validate diastolic value
+ final diastolicValue = diastolicController.text.trim();
+ if (diastolicValue.isEmpty) {
+ return "Please enter diastolic value";
+ }
+ final parsedDiastolic = int.tryParse(diastolicValue);
+ if (parsedDiastolic == null || parsedDiastolic <= 0) {
+ return "Please enter a valid diastolic value";
+ }
+
+ // Validate arm selection
+ if (_selectedMeasuredArm.isEmpty) {
+ return "Please select measured arm";
+ }
+
+ // Validate date time
+ if (dateTime.trim().isEmpty) {
+ return "Please select date and time";
+ }
+
+ return null; // No errors
+ }
+
+ // Save blood pressure entry with validation
+ Future saveBloodPressureEntry({
+ required String dateTime,
+ Function()? onSuccess,
+ Function(String error)? onFailure,
+ }) async {
+ // Validate
+ final validationError = _validateBloodPressureEntry(dateTime);
+ if (validationError != null) {
+ _errorMessage = validationError;
+ if (onFailure != null) onFailure(validationError);
+ return;
+ }
+
+ // Clear previous error and show loading
+ _errorMessage = null;
+ isLoading = true;
+ notifyListeners();
+
+ try {
+ // Get measured arm index (0 = Left Arm, 1 = Right Arm)
+ final measuredArmIndex = measuredArmList.indexOf(_selectedMeasuredArm);
+
+ // Call API
+ final success = await addOrUpdateBloodPressureResult(
+ bloodPressureDate: dateTime,
+ systolicePressure: systolicController.text.trim(),
+ diastolicPressure: diastolicController.text.trim(),
+ measuredArm: measuredArmIndex >= 0 ? measuredArmIndex : 0,
+ isUpdate: false,
+ );
+
+ isLoading = false;
+
+ if (success) {
+ // Clear form after successful save
+ systolicController.clear();
+ diastolicController.clear();
+ _selectedMeasuredArm = '';
+ notifyListeners();
+ if (onSuccess != null) onSuccess();
+ } else {
+ _errorMessage = "Failed to save blood pressure entry. Please try again";
+ notifyListeners();
+ if (onFailure != null) onFailure(_errorMessage!);
+ }
+ } catch (e) {
+ log('Error in saveBloodPressureEntry: $e');
+ _errorMessage = "An error occurred. Please try again";
+ isLoading = false;
+ notifyListeners();
+ if (onFailure != null) onFailure(_errorMessage!);
+ }
+ }
+
+ @override
+ void dispose() {
+ bloodSugarController.dispose();
+ weightController.dispose();
+ systolicController.dispose();
+ diastolicController.dispose();
+ super.dispose();
+ }
+}
diff --git a/lib/presentation/health_trackers/widgets/tracker_last_value_card.dart b/lib/presentation/health_trackers/widgets/tracker_last_value_card.dart
new file mode 100644
index 0000000..542baa5
--- /dev/null
+++ b/lib/presentation/health_trackers/widgets/tracker_last_value_card.dart
@@ -0,0 +1,271 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.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/enums.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/presentation/health_trackers/health_trackers_view_model.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
+import 'package:provider/provider.dart';
+import 'package:shimmer/shimmer.dart';
+
+class TrackerLastValueCard extends StatelessWidget {
+ final HealthTrackerTypeEnum trackerType;
+
+ const TrackerLastValueCard({super.key, required this.trackerType});
+
+ /// Get status text and color based on blood sugar value
+ (String status, Color color, Color bgColor) _getBloodSugarStatus(double value) {
+ if (value < 70) {
+ return ('Low'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.5));
+ } else if (value <= 100) {
+ return ('Normal'.needTranslation, AppColors.successColor, AppColors.successLightBgColor);
+ } else if (value <= 125) {
+ return ('Pre-diabetic'.needTranslation, AppColors.ratingColorYellow, AppColors.errorColor.withValues(alpha: 0.4));
+ } else {
+ return ('High'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.4));
+ }
+ }
+
+ /// Get status text and color based on blood pressure value (systolic)
+ (String status, Color color, Color bgColor) _getBloodPressureStatus(int systolic) {
+ if (systolic < 90) {
+ return ('Low'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.5));
+ } else if (systolic <= 120) {
+ return ('Normal'.needTranslation, AppColors.successColor, AppColors.successLightBgColor);
+ } else if (systolic <= 140) {
+ return ('Elevated'.needTranslation, AppColors.ratingColorYellow, AppColors.errorColor.withValues(alpha: 0.4));
+ } else {
+ return ('High'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.4));
+ }
+ }
+
+ /// Get status for weight (neutral - no good/bad status)
+ (String status, Color color, Color bgColor) _getWeightStatus() {
+ return ('Recorded'.needTranslation, AppColors.successColor, AppColors.successLightBgColor);
+ }
+
+ /// Get default unit based on tracker type
+ String _getDefaultUnit() {
+ switch (trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ return 'mg/dL';
+ case HealthTrackerTypeEnum.bloodPressure:
+ return 'mmHg';
+ case HealthTrackerTypeEnum.weightTracker:
+ return 'kg';
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Consumer(
+ builder: (context, viewModel, child) {
+ // Get the last record based on tracker type
+ dynamic lastRecord;
+ String displayValue = '--';
+ String unit = _getDefaultUnit();
+ DateTime? lastDate;
+ String status = '';
+ Color statusColor = AppColors.greyTextColor;
+
+ switch (trackerType) {
+ case HealthTrackerTypeEnum.bloodSugar:
+ final allResults = [
+ ...viewModel.weekDiabeticPatientResult,
+ ...viewModel.monthDiabeticPatientResult,
+ ...viewModel.yearDiabeticPatientResult,
+ ];
+ if (allResults.isNotEmpty) {
+ allResults.sort((a, b) {
+ final dateA = a.dateChart ?? DateTime(1900);
+ final dateB = b.dateChart ?? DateTime(1900);
+ return dateB.compareTo(dateA);
+ });
+ lastRecord = allResults.first;
+ final lastValue = lastRecord.resultValue?.toDouble() ?? 0.0;
+ displayValue = lastValue.toStringAsFixed(0);
+ unit = lastRecord.unit ?? 'mg/dL';
+ lastDate = lastRecord.dateChart;
+ final (s, c, _) = _getBloodSugarStatus(lastValue);
+ status = s;
+ statusColor = c;
+ }
+ break;
+
+ case HealthTrackerTypeEnum.bloodPressure:
+ final allResults = [
+ ...viewModel.weekBloodPressureResult,
+ ...viewModel.monthBloodPressureResult,
+ ...viewModel.yearBloodPressureResult,
+ ];
+ if (allResults.isNotEmpty) {
+ allResults.sort((a, b) {
+ final dateA = a.bloodPressureDate ?? DateTime(1900);
+ final dateB = b.bloodPressureDate ?? DateTime(1900);
+ return dateB.compareTo(dateA);
+ });
+ lastRecord = allResults.first;
+ final systolic = lastRecord.systolicePressure ?? 0;
+ final diastolic = lastRecord.diastolicPressure ?? 0;
+ displayValue = '$systolic/$diastolic';
+ unit = 'mmHg';
+ lastDate = lastRecord.bloodPressureDate;
+ final (s, c, _) = _getBloodPressureStatus(systolic);
+ status = s;
+ statusColor = c;
+ }
+ break;
+
+ case HealthTrackerTypeEnum.weightTracker:
+ final allResults = [
+ ...viewModel.weekWeightMeasurementResult,
+ ...viewModel.monthWeightMeasurementResult,
+ ...viewModel.yearWeightMeasurementResult,
+ ];
+ if (allResults.isNotEmpty) {
+ allResults.sort((a, b) {
+ final dateA = a.weightDate ?? DateTime(1900);
+ final dateB = b.weightDate ?? DateTime(1900);
+ return dateB.compareTo(dateA);
+ });
+ lastRecord = allResults.first;
+ final weightValue = lastRecord.weightMeasured?.toDouble() ?? 0.0;
+ displayValue = weightValue.toStringAsFixed(0);
+ unit = lastRecord.unit ?? 'kg';
+ lastDate = lastRecord.weightDate;
+ final (s, c, _) = _getWeightStatus();
+ status = s;
+ statusColor = c;
+ }
+ break;
+ }
+
+ final formattedDate = lastDate != null ? DateFormat('EEE DD MMM, yy').format(lastDate) : DateFormat('EEE DD MMM, yy').format(DateTime.now());
+
+ // Show shimmer while loading
+ if (viewModel.isLoading) {
+ return Container(
+ margin: EdgeInsets.symmetric(horizontal: 24.w),
+ padding: EdgeInsets.all(16.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: Shimmer.fromColors(
+ baseColor: AppColors.shimmerBaseColor,
+ highlightColor: AppColors.shimmerHighlightColor,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ height: 40.h,
+ width: 120.w,
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: BorderRadius.circular(8.r),
+ ),
+ ),
+ SizedBox(height: 8.h),
+ Row(
+ children: [
+ Container(
+ height: 32.h,
+ width: 150.w,
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: BorderRadius.circular(16.r),
+ ),
+ ),
+ SizedBox(width: 8.w),
+ Container(
+ height: 32.h,
+ width: 80.w,
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: BorderRadius.circular(16.r),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ // Show empty state if no records
+ if (lastRecord == null) {
+ return Container(
+ margin: EdgeInsets.symmetric(horizontal: 24.w),
+ padding: EdgeInsets.all(16.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ "--".toText32(isBold: true, color: AppColors.greyTextColor),
+ SizedBox(width: 6.w),
+ unit.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500).paddingOnly(top: 8.h),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ AppCustomChipWidget(
+ labelText: "No records yet".needTranslation,
+ icon: AppAssets.doctor_calendar_icon,
+ ),
+ ],
+ ),
+ );
+ }
+
+ return Container(
+ margin: EdgeInsets.symmetric(horizontal: 24.w),
+ padding: EdgeInsets.all(16.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ displayValue.toText32(isBold: true, color: statusColor),
+ SizedBox(width: 6.w),
+ unit.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500).paddingOnly(top: 8.h),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Row(
+ children: [
+ AppCustomChipWidget(
+ labelText: "${"Last Record".needTranslation}: $formattedDate",
+ icon: AppAssets.doctor_calendar_icon,
+ ),
+ SizedBox(width: 8.w),
+ if (trackerType != HealthTrackerTypeEnum.weightTracker) ...[
+ AppCustomChipWidget(
+ labelText: status.needTranslation,
+ icon: AppAssets.normalStatusGreenIcon,
+ iconColor: statusColor,
+ ),
+ ]
+ ],
+ ),
+ ],
+ ),
+ );
+ },
+ );
+ }
+}
diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart
index 3b31d38..a5903db 100644
--- a/lib/presentation/hmg_services/services_page.dart
+++ b/lib/presentation/hmg_services/services_page.dart
@@ -45,12 +45,13 @@ class ServicesPage extends StatelessWidget {
late final List hmgServices = [
HmgServicesComponentModel(
- 11,
- "Emergency Services".needTranslation,
- "".needTranslation,
- AppAssets.emergency_services_icon,
- bgColor: AppColors.primaryRedColor,
- true, route: null, onTap: () {
+ 11,
+ "Emergency Services".needTranslation,
+ "".needTranslation,
+ AppAssets.emergency_services_icon,
+ bgColor: AppColors.primaryRedColor,
+ true,
+ route: null, onTap: () {
getIt.get().flushData();
getIt.get().getTransportationOrders(
showLoader: false,
@@ -162,6 +163,15 @@ class ServicesPage extends StatelessWidget {
];
late final List