pull/206/head
haroon amjad 3 days ago
parent c18bf9f2c1
commit 93af246700

@ -1637,5 +1637,12 @@
"emergencyServiceHeader": "تسجيل الوصول إلى قسم الطوارئ، أسرع من أي وقت مضى. اتصل بالإسعاف / فريق الاستجابة السريعة على الفور", "emergencyServiceHeader": "تسجيل الوصول إلى قسم الطوارئ، أسرع من أي وقت مضى. اتصل بالإسعاف / فريق الاستجابة السريعة على الفور",
"emergencyServiceDescription": "هل تواجه حالة طبية طارئة؟ سيارات الإسعاف وفرق الاستجابة السريعة لدينا جاهزة على مدار الساعة. بالإضافة إلى ذلك، يمكنك تسجيل دخولك إلى قسم الطوارئ بسرعة لتلقي رعاية أسرع.", "emergencyServiceDescription": "هل تواجه حالة طبية طارئة؟ سيارات الإسعاف وفرق الاستجابة السريعة لدينا جاهزة على مدار الساعة. بالإضافة إلى ذلك، يمكنك تسجيل دخولك إلى قسم الطوارئ بسرعة لتلقي رعاية أسرع.",
"homeHealthCareServiceHeader": "صحتك، في أبهى صورها. رعاية فائقة الجودة، تصلك إلى عتبة دارك.", "homeHealthCareServiceHeader": "صحتك، في أبهى صورها. رعاية فائقة الجودة، تصلك إلى عتبة دارك.",
"homeHealthCareServiceDescription": "نقدم لكم رعاية صحية عالية الجودة تصلكم إلى عتبة منزلكم. ممرضات ذوات خبرة يقدمون رعاية حانية في راحة منزلكم." "homeHealthCareServiceDescription": "نقدم لكم رعاية صحية عالية الجودة تصلكم إلى عتبة منزلكم. ممرضات ذوات خبرة يقدمون رعاية حانية في راحة منزلكم.",
"profileOnlyText": "الملف الشخصي",
"information": "معلومة",
"noFavouriteDoctors": "ليس لديك أي أطباء مفضلين حتى الآن",
"addDoctors": "إضافة الأطباء",
"favouriteList": "قائمة المفضلة",
"later": "لاحقاً"
} }

@ -1612,28 +1612,28 @@
"sortByNearestLocation": "Sort by nearest to your location", "sortByNearestLocation": "Sort by nearest to your location",
"giveLocationPermissionForNearestList": "Please grant location permission from app settings to see the nearest locations.", "giveLocationPermissionForNearestList": "Please grant location permission from app settings to see the nearest locations.",
"communicationLanguage": "Communication Language", "communicationLanguage": "Communication Language",
"cmcServiceHeader": "Complete Health Checkup: Advanced diagnostics, Detailed Health insights", "cmcServiceHeader": "Complete Health Checkup: Advanced diagnostics, Detailed Health insights",
"cmcServiceDescription": "Get detailed insights into your health with our advanced diagnostics. Understand your body better for a healthier future.", "cmcServiceDescription": "Get detailed insights into your health with our advanced diagnostics. Understand your body better for a healthier future.",
"eReferralServiceHeader": "HMG Hospital E-Referral: Streamlined patient referrals", "eReferralServiceHeader": "HMG Hospital E-Referral: Streamlined patient referrals",
"eReferralServiceDescription": "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.", "eReferralServiceDescription": "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.",
"bloodDonationServiceHeader": "Give Blood, Save Lives. Your donation makes a difference.", "bloodDonationServiceHeader": "Give Blood, Save Lives. Your donation makes a difference.",
"bloodDonationServiceDescription": "Donate blood, empower lives. Your contribution creates hope. Join our blood drive and be a lifeline for those in need. Every drop counts!", "bloodDonationServiceDescription": "Donate blood, empower lives. Your contribution creates hope. Join our blood drive and be a lifeline for those in need. Every drop counts!",
"healthTrackersServiceHeader": "Track Your Vitals with Ease and effortlessly ", "healthTrackersServiceHeader": "Track Your Vitals with Ease and effortlessly ",
"healthTrackersServiceDescription": "Input your metrics for continuous heart rate monitoring, blood pressure and precise daily activity summaries. Stay informed and optimize your well-being with ease.", "healthTrackersServiceDescription": "Input your metrics for continuous heart rate monitoring, blood pressure and precise daily activity summaries. Stay informed and optimize your well-being with ease.",
"waterConsumptionServiceHeader": "Stay Hydrated, Stay Healthy. Track your daily water intake with ease.", "waterConsumptionServiceHeader": "Stay Hydrated, Stay Healthy. Track your daily water intake with ease.",
"waterConsumptionServiceDescription": "Quench your thirst, track your health. Effortlessly monitor your daily water intake with our user-friendly app, ensuring optimal hydration and well-being.", "waterConsumptionServiceDescription": "Quench your thirst, track your health. Effortlessly monitor your daily water intake with our user-friendly app, ensuring optimal hydration and well-being.",
"smartWatchServiceHeader": "Sync Your Smartwatch with Health Apps", "smartWatchServiceHeader": "Sync Your Smartwatch with Health Apps",
"smartWatchServiceDescription": "Seamlessly connect your smartwatch to our health app for effortless data tracking and personalized insights.", "smartWatchServiceDescription": "Seamlessly connect your smartwatch to our health app for effortless data tracking and personalized insights.",
"liveChatServiceHeader": "24/7 Expert\nAssistance", "liveChatServiceHeader": "24/7 Expert\nAssistance",
"liveChatServiceDescription": "Need help ? Our live chat connects you with expert support for any questions about features, settings, or troubleshooting.", "liveChatServiceDescription": "Need help ? Our live chat connects you with expert support for any questions about features, settings, or troubleshooting.",
"emergencyServiceHeader": "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly", "emergencyServiceHeader": "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly",
"emergencyServiceDescription": "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.", "emergencyServiceDescription": "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.",
"homeHealthCareServiceHeader": "Your Health, Elevated. Premium care, delivered to your doorstep.", "homeHealthCareServiceHeader": "Your Health, Elevated. Premium care, delivered to your doorstep.",
"homeHealthCareServiceDescription": "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home." "homeHealthCareServiceDescription": "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.",
"profileOnlyText": "Profile",
"information": "Information",
"noFavouriteDoctors": "You don't have any favourite doctors yet",
"addDoctors": "Add Doctors",
"favouriteList": "Favourite List",
"later": "Later"
} }

@ -200,7 +200,7 @@ class ApiClientImp implements ApiClient {
} }
// body['TokenID'] = "@dm!n"; // body['TokenID'] = "@dm!n";
// body['PatientID'] = 3628599; // body['PatientID'] = 1018977;
// body['PatientTypeID'] = 1; // body['PatientTypeID'] = 1;
// body['PatientOutSA'] = 0; // body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q"; // body['SessionID'] = "45786230487560q";

@ -768,6 +768,7 @@ class Utils {
return Row( return Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
spacing: 5.w,
children: [ children: [
Image.asset(AppAssets.mada, width: 35.h, height: 35.h), Image.asset(AppAssets.mada, width: 35.h, height: 35.h),
Image.asset( Image.asset(

@ -157,7 +157,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
LaserCategoryType(11, 'retouch'), LaserCategoryType(11, 'retouch'),
]; ];
List<LaserCategoryType> maleLaserCategory = [ List<LaserCategoryType> maleLaserCategory = [
LaserCategoryType(1, 'body'), LaserCategoryType(1, 'bodyString'),
LaserCategoryType(2, 'face'), LaserCategoryType(2, 'face'),
LaserCategoryType(11, 'retouch'), LaserCategoryType(11, 'retouch'),
]; ];

@ -61,4 +61,14 @@ class LaserBodyPart {
data['CategoryNameN'] = this.categoryNameN; data['CategoryNameN'] = this.categoryNameN;
return data; return data;
} }
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is LaserBodyPart &&
runtimeType == other.runtimeType &&
id == other.id;
@override
int get hashCode => id.hashCode;
} }

@ -4,6 +4,7 @@ import 'dart:core';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart';
@ -87,6 +88,9 @@ class LabViewModel extends ChangeNotifier {
LabOrderResponseByAi? labOrderResponseByAi; LabOrderResponseByAi? labOrderResponseByAi;
LabOrdersResponseByAi? labOrdersResponseByAi; LabOrdersResponseByAi? labOrdersResponseByAi;
bool isLabAIAnalysisNeedsToBeShown = true;
bool isLabResultsHistoryShowMore = false;
LabViewModel({required this.labRepo, required this.errorHandlerService, required this.navigationService}); LabViewModel({required this.labRepo, required this.errorHandlerService, required this.navigationService});
initLabProvider() { initLabProvider() {
@ -96,6 +100,8 @@ class LabViewModel extends ChangeNotifier {
labOrderTests.clear(); labOrderTests.clear();
isLabOrdersLoading = true; isLabOrdersLoading = true;
isLabResultsLoading = true; isLabResultsLoading = true;
isLabAIAnalysisNeedsToBeShown = true;
isLabResultsHistoryShowMore = false;
patientLabOrdersByClinic.clear(); patientLabOrdersByClinic.clear();
patientLabOrdersByHospital.clear(); patientLabOrdersByHospital.clear();
patientLabOrdersViewList.clear(); patientLabOrdersViewList.clear();
@ -114,6 +120,16 @@ class LabViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setIsLabAIAnalysisNeedsToBeShown(bool isLabAIAnalysisNeedsToBeShown) {
this.isLabAIAnalysisNeedsToBeShown = isLabAIAnalysisNeedsToBeShown;
notifyListeners();
}
setIsLabResultsHistoryShowMore() {
isLabResultsHistoryShowMore = !isLabResultsHistoryShowMore;
notifyListeners();
}
void setIsSortByClinic(bool value) { void setIsSortByClinic(bool value) {
isSortByClinic = value; isSortByClinic = value;
patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital; patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
@ -806,7 +822,13 @@ class LabViewModel extends ChangeNotifier {
Future<void> getAiOverviewLabOrders({required PatientLabOrdersResponseModel labOrder, required String loadingText}) async { Future<void> getAiOverviewLabOrders({required PatientLabOrdersResponseModel labOrder, required String loadingText}) async {
// LoadingUtils.showFullScreenLoader(loadingText: "Loading and analysing your data,\nPlease be patient and let the AI do the magic. \nPlease be patient, This might take some time."); // LoadingUtils.showFullScreenLoader(loadingText: "Loading and analysing your data,\nPlease be patient and let the AI do the magic. \nPlease be patient, This might take some time.");
LoadingUtils.showFullScreenLoader(loadingText: loadingText); // LoadingUtils.showFullScreenLoader(loadingText: loadingText);
LoaderBottomSheet.showLoader(
loadingText: loadingText,
showCloseButton: true,
onCloseTap: () {
setIsLabAIAnalysisNeedsToBeShown(false);
});
List<Map<String, dynamic>> results = []; List<Map<String, dynamic>> results = [];
Map<String, dynamic> orderData = {"order_date": labOrder.orderDate ?? "", "clinic": labOrder.clinicDescription ?? "", "doctor": labOrder.doctorName ?? "", "results": []}; Map<String, dynamic> orderData = {"order_date": labOrder.orderDate ?? "", "clinic": labOrder.clinicDescription ?? "", "doctor": labOrder.doctorName ?? "", "results": []};
List<Map<String, dynamic>> testResults = []; List<Map<String, dynamic>> testResults = [];
@ -829,16 +851,20 @@ class LabViewModel extends ChangeNotifier {
result.fold( result.fold(
(failure) async { (failure) async {
LoadingUtils.hideFullScreenLoader(); LoadingUtils.hideFullScreenLoader();
await errorHandlerService.handleError(failure: failure); if (isLabAIAnalysisNeedsToBeShown) {
await errorHandlerService.handleError(failure: failure);
}
}, },
(apiResponse) { (apiResponse) {
LoadingUtils.hideFullScreenLoader(); LoadingUtils.hideFullScreenLoader();
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
labOrdersResponseByAi = apiResponse.data; if (isLabAIAnalysisNeedsToBeShown) {
navigationService.push( labOrdersResponseByAi = apiResponse.data;
MaterialPageRoute(builder: (_) => LabAiAnalysisDetailedPage()), navigationService.push(
); MaterialPageRoute(builder: (_) => LabAiAnalysisDetailedPage()),
);
}
} }
}, },
); );
@ -847,7 +873,12 @@ class LabViewModel extends ChangeNotifier {
} }
Future<void> getAiOverviewSingleLabResult({required String langId, required LabResult recentLabResult, required String loadingText}) async { Future<void> getAiOverviewSingleLabResult({required String langId, required LabResult recentLabResult, required String loadingText}) async {
LoaderBottomSheet.showLoader(loadingText: loadingText); LoaderBottomSheet.showLoader(
loadingText: loadingText,
showCloseButton: true,
onCloseTap: () {
setIsLabAIAnalysisNeedsToBeShown(false);
});
List<Map<String, dynamic>> results = []; List<Map<String, dynamic>> results = [];
results.add({ results.add({
"Description": recentLabResult.description ?? '', "Description": recentLabResult.description ?? '',
@ -857,19 +888,24 @@ class LabViewModel extends ChangeNotifier {
"ReferanceRange": recentLabResult.referanceRange ?? '', "ReferanceRange": recentLabResult.referanceRange ?? '',
}); });
var payload = {"patient_id": currentlySelectedPatientOrder!.patientID, "language_id": langId, "lab_results": results}; // var payload = {"patient_id": currentlySelectedPatientOrder!.patientID, "language_id": langId, "lab_results": results};
var payload = {"patient_id": getIt.get<AppState>().getAuthenticatedUser()!.patientId, "language_id": langId, "lab_results": results};
final result = await labRepo.getPatientAiOverViewLabOrder(payload); final result = await labRepo.getPatientAiOverViewLabOrder(payload);
result.fold( result.fold(
(failure) async { (failure) async {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
await errorHandlerService.handleError(failure: failure); if (isLabAIAnalysisNeedsToBeShown) {
await errorHandlerService.handleError(failure: failure);
}
}, },
(apiResponse) { (apiResponse) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
labOrderResponseByAi = apiResponse.data; if (isLabAIAnalysisNeedsToBeShown) {
notifyListeners(); labOrderResponseByAi = apiResponse.data;
notifyListeners();
}
} }
}, },
); );

@ -87,6 +87,8 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
hmcCount = registeredDoctorMap.hmcSize; hmcCount = registeredDoctorMap.hmcSize;
hmgCount = registeredDoctorMap.hmgSize; hmgCount = registeredDoctorMap.hmgSize;
hospitalList!.sort((a, b) => num.parse(a.distanceInKMs!).compareTo(num.parse(b.distanceInKMs!)));
getDisplayList(); getDisplayList();
} }

@ -12,6 +12,10 @@ abstract class NotificationsRepo {
required int pagingSize, required int pagingSize,
required int currentPage, required int currentPage,
}); });
Future<Either<Failure, GenericApiModel<List<NotificationResponseModel>>>> markAsRead({
required int notificationID,
});
} }
class NotificationsRepoImp implements NotificationsRepo { class NotificationsRepoImp implements NotificationsRepo {
@ -75,4 +79,38 @@ class NotificationsRepoImp implements NotificationsRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel<List<NotificationResponseModel>>>> markAsRead({required int notificationID}) async {
Map<String, dynamic> mapDevice = {"NotificationPoolID": notificationID};
try {
GenericApiModel<List<NotificationResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
PUSH_NOTIFICATION_SET_MESSAGES_FROM_POOL_AS_READ,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<List<NotificationResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: [],
);
} 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()));
}
}
} }

@ -89,6 +89,21 @@ class NotificationsViewModel extends ChangeNotifier {
); );
} }
Future<void> markAsRead({
int notificationID = 0,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await notificationsRepo.markAsRead(notificationID: notificationID);
result.fold(
(failure) async {},
(apiResponse) {
refreshNotifications();
},
);
}
Future<void> refreshNotifications({ Future<void> refreshNotifications({
Function(dynamic)? onSuccess, Function(dynamic)? onSuccess,
Function(String)? onError, Function(String)? onError,

@ -1630,5 +1630,11 @@ abstract class LocaleKeys {
static const emergencyServiceDescription = 'emergencyServiceDescription'; static const emergencyServiceDescription = 'emergencyServiceDescription';
static const homeHealthCareServiceHeader = 'homeHealthCareServiceHeader'; static const homeHealthCareServiceHeader = 'homeHealthCareServiceHeader';
static const homeHealthCareServiceDescription = 'homeHealthCareServiceDescription'; static const homeHealthCareServiceDescription = 'homeHealthCareServiceDescription';
static const profileOnlyText = 'profileOnlyText';
static const information = 'information';
static const noFavouriteDoctors = 'noFavouriteDoctors';
static const addDoctors = 'addDoctors';
static const favouriteList = 'favouriteList';
static const later = 'later';
} }

@ -73,10 +73,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
void initState() { void initState() {
scheduleMicrotask(() async { scheduleMicrotask(() async {
if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) { if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) {
CalenderUtilsNew calendarUtils = await CalenderUtilsNew.instance; // CalenderUtilsNew calendarUtils = await CalenderUtilsNew.instance;
var doesExist = await calendarUtils.checkIfEventExist("${widget.patientAppointmentHistoryResponseModel.appointmentNo}"); // var doesExist = await calendarUtils.checkIfEventExist("${widget.patientAppointmentHistoryResponseModel.appointmentNo}");
myAppointmentsViewModel.setAppointmentReminder(doesExist, widget.patientAppointmentHistoryResponseModel); // myAppointmentsViewModel.setAppointmentReminder(doesExist, widget.patientAppointmentHistoryResponseModel);
setState(() {}); // setState(() {});
} }
}); });

@ -206,7 +206,7 @@ class _SavedLogin extends State<SavedLogin> {
}, },
backgroundColor: AppColors.primaryRedColor, backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor,
textColor: AppColors.textColor, textColor: Colors.white,
icon: AppAssets.sms), icon: AppAssets.sms),
), ),
Row( Row(

@ -276,7 +276,46 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
// Show empty state or actual list // Show empty state or actual list
return myAppointmentsVM.patientFavouriteDoctorsList.isEmpty return myAppointmentsVM.patientFavouriteDoctorsList.isEmpty
? SizedBox() ? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
LocaleKeys.favouriteList.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
padding: EdgeInsets.all(16.h),
child: Column(
children: [
Center(
child: LocaleKeys.noFavouriteDoctors.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
),
SizedBox(height: 12.h),
CustomButton(
text: LocaleKeys.add.tr(context: context),
onPressed: () {
Navigator.of(context).push(CustomPageRoute(page: SearchDoctorByName()));
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 14.f,
fontWeight: FontWeight.w500,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor,
height: 40.h,
),
],
),
).paddingSymmetrical(24.w, 0.h),
SizedBox(height: 24.h),
],
)
: Column( : Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -671,7 +710,7 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
return SizedBox.shrink(); return SizedBox.shrink();
} }
void _handleSortByLocationToggle(bool value, AppointmentViaRegionViewmodel regionVM) { void _handleSortByLocationToggle(bool value, AppointmentViaRegionViewmodel regionVM) {
if (value) { if (value) {
final locationUtils = getIt.get<LocationUtils>(); final locationUtils = getIt.get<LocationUtils>();
locationUtils.getLocation( locationUtils.getLocation(

@ -33,7 +33,7 @@ class DoctorProfilePage extends StatelessWidget {
children: [ children: [
Expanded( Expanded(
child: CollapsingListView( child: CollapsingListView(
title: LocaleKeys.doctorProfile.tr(), title: LocaleKeys.profileOnlyText.tr(),
trailing: appState.isAuthenticated ? Consumer<BookAppointmentsViewModel>( trailing: appState.isAuthenticated ? Consumer<BookAppointmentsViewModel>(
builder: (context, viewModel, child) { builder: (context, viewModel, child) {
return SizedBox( return SizedBox(
@ -178,7 +178,7 @@ class DoctorProfilePage extends StatelessWidget {
SizedBox(height: 16.h), SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.docInfo.tr(context: context).toText14(weight: FontWeight.w600, color: AppColors.textColor), LocaleKeys.information.tr(context: context).toText14(weight: FontWeight.w600, color: AppColors.textColor),
SizedBox(height: 6.h), SizedBox(height: 6.h),
bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo!.toText12(fontWeight: FontWeight.w600, color: AppColors.greyTextColor), bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo!.toText12(fontWeight: FontWeight.w600, color: AppColors.greyTextColor),
SizedBox(height: 24.h), SizedBox(height: 24.h),

@ -84,7 +84,7 @@ class LaserAppointment extends StatelessWidget {
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [ tabs: [
CustomTabBarModel(null, LocaleKeys.malE.tr()), CustomTabBarModel(null, LocaleKeys.malE.tr()),
CustomTabBarModel(null, "Female"), CustomTabBarModel(null, LocaleKeys.femaleGender.tr()),
], ],
onTabChange: (index) { onTabChange: (index) {
var viewmodel = context.read<BookAppointmentsViewModel>(); var viewmodel = context.read<BookAppointmentsViewModel>();

@ -1132,6 +1132,7 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID?.toString() ?? ""; id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID?.toString() ?? "";
} }
if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) { if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) {
bookAppointmentsViewModel.setProjectID(id);
if (appState.isAuthenticated) { if (appState.isAuthenticated) {
initDentalAppointment(); initDentalAppointment();
return SizedBox.shrink(); return SizedBox.shrink();

@ -21,6 +21,7 @@ class RrtRequestTypeSelect extends StatelessWidget {
return Column( return Column(
children: [ children: [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
LocaleKeys.rapidResponseTeam.tr(context: context).toText20(color: AppColors.textColor, isBold: true), LocaleKeys.rapidResponseTeam.tr(context: context).toText20(color: AppColors.textColor, isBold: true),
SizedBox( SizedBox(
@ -32,10 +33,9 @@ class RrtRequestTypeSelect extends StatelessWidget {
borderRadius: 20.r, borderRadius: 20.r,
), ),
child: OptionSelection(context)), child: OptionSelection(context)),
SizedBox( SizedBox(height: 16.h),
height: 24.h,
),
termsAndCondition(context, emergencyServicesVM), termsAndCondition(context, emergencyServicesVM),
SizedBox(height: 16.h),
], ],
).paddingSymmetrical(24.w, 0.h), ).paddingSymmetrical(24.w, 0.h),
bottomPriceContent(context, emergencyServicesVM) bottomPriceContent(context, emergencyServicesVM)
@ -122,15 +122,7 @@ class RrtRequestTypeSelect extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Row( Utils.getPaymentMethods(),
spacing: 6.w,
children: [
Image.asset(AppAssets.mada, width: 24.h, height: 24.h),
Image.asset(AppAssets.visa, width: 24.h, height: 24.h),
Image.asset(AppAssets.mastercard, width: 24.h, height: 24.h),
Image.asset(AppAssets.applePay, width: 24.h, height: 24.h),
],
),
Column( Column(
children: [ children: [
Divider( Divider(
@ -149,7 +141,9 @@ class RrtRequestTypeSelect extends StatelessWidget {
CustomButton(text: LocaleKeys.next.tr(), onPressed: () { CustomButton(text: LocaleKeys.next.tr(), onPressed: () {
Navigator.pop(context); Navigator.pop(context);
emergencyServicesVM.openRRT(); emergencyServicesVM.openRRT();
}) },
isDisabled: !emergencyServicesVM.agreedToTermsAndCondition,
)
], ],
).paddingAll(24.h), ).paddingAll(24.h),
), ),
@ -236,7 +230,6 @@ class RrtRequestTypeSelect extends StatelessWidget {
value: emergencyServicesVM.agreedToTermsAndCondition, value: emergencyServicesVM.agreedToTermsAndCondition,
checkColor: AppColors.whiteColor, checkColor: AppColors.whiteColor,
fillColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) { fillColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) {
print("the state is ${states}");
if (states.contains(WidgetState.selected)) { if (states.contains(WidgetState.selected)) {
return AppColors.errorColor; return AppColors.errorColor;
} }

@ -133,22 +133,22 @@ class _LandingPageState extends State<LandingPage> {
// Commented as per new requirement to remove rating popup from the app // Commented as per new requirement to remove rating popup from the app
// if(!appState.isRatedVisible) { if(!appState.isRatedVisible) {
// appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) { appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) {
// if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) { if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) {
// appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!, appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!,
// onSuccess: ((response) { onSuccess: ((response) {
// appointmentRatingViewModel.setClinicOrDoctor(false); appointmentRatingViewModel.setClinicOrDoctor(false);
// appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation); appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr(context: context));
// appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation); appointmentRatingViewModel.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context));
// openLastRating(); openLastRating();
// appState.setRatedVisible(true); appState.setRatedVisible(true);
// }), }),
// ); );
// } }
// }, },
// ); );
// } }
} }
}); });
super.initState(); super.initState();
@ -173,7 +173,7 @@ class _LandingPageState extends State<LandingPage> {
children: [ children: [
SingleChildScrollView( SingleChildScrollView(
padding: EdgeInsets.only( padding: EdgeInsets.only(
top: (!insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown) top: (appState.isAuthenticated && !insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown)
? (MediaQuery.paddingOf(context).top + 70.h) ? (MediaQuery.paddingOf(context).top + 70.h)
: kToolbarHeight + 0.h, : kToolbarHeight + 0.h,
bottom: 24), bottom: 24),
@ -727,7 +727,7 @@ class _LandingPageState extends State<LandingPage> {
], ],
), ),
), ),
(!insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown) (appState.isAuthenticated && !insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown)
? Container( ? Container(
height: MediaQuery.paddingOf(context).top + 50.h, height: MediaQuery.paddingOf(context).top + 50.h,
decoration: ShapeDecoration( decoration: ShapeDecoration(

@ -74,6 +74,7 @@ class _InsuranceHomePageState extends State<InsuranceHomePage> {
return Column( return Column(
children: [ children: [
PatientInsuranceCard( PatientInsuranceCard(
isShowButtons: index == 0,
insuranceCardDetailsModel: insuranceVM.patientInsuranceList[index], insuranceCardDetailsModel: insuranceVM.patientInsuranceList[index],
isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo))), isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo))),
SizedBox( SizedBox(

@ -20,10 +20,11 @@ import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
class PatientInsuranceCard extends StatelessWidget { class PatientInsuranceCard extends StatelessWidget {
PatientInsuranceCard({super.key, required this.insuranceCardDetailsModel, required this.isInsuranceExpired}); PatientInsuranceCard({super.key, required this.insuranceCardDetailsModel, required this.isInsuranceExpired, this.isShowButtons = true});
PatientInsuranceDetailsResponseModel insuranceCardDetailsModel; PatientInsuranceDetailsResponseModel insuranceCardDetailsModel;
bool isInsuranceExpired = false; bool isInsuranceExpired = false;
bool isShowButtons = true;
late InsuranceViewModel insuranceViewModel; late InsuranceViewModel insuranceViewModel;
late AppState appState; late AppState appState;
@ -60,7 +61,7 @@ class PatientInsuranceCard extends StatelessWidget {
), ),
], ],
), ),
AppCustomChipWidget( isShowButtons ? AppCustomChipWidget(
icon: isCurrentPatientInsuranceExpired(insuranceCardDetailsModel.cardValidTo!) icon: isCurrentPatientInsuranceExpired(insuranceCardDetailsModel.cardValidTo!)
? AppAssets.cancel_circle_icon ? AppAssets.cancel_circle_icon
: insuranceViewModel.isInsuranceActive : insuranceViewModel.isInsuranceActive
@ -120,7 +121,7 @@ class PatientInsuranceCard extends StatelessWidget {
? AppColors.successColor.withOpacity(0.1) ? AppColors.successColor.withOpacity(0.1)
: AppColors.warningColorYellow.withOpacity(0.1), : AppColors.warningColorYellow.withOpacity(0.1),
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: insuranceViewModel.isInsuranceActive ? 6.w : 0.w), labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: insuranceViewModel.isInsuranceActive ? 6.w : 0.w),
).toShimmer2(isShow: insuranceViewModel.isInsuranceLoading), ).toShimmer2(isShow: insuranceViewModel.isInsuranceLoading) : SizedBox.shrink(),
// AppCustomChipWidget( // AppCustomChipWidget(
// icon: isInsuranceExpired ? AppAssets.cancel_circle_icon : AppAssets.insurance_active_icon, // icon: isInsuranceExpired ? AppAssets.cancel_circle_icon : AppAssets.insurance_active_icon,
// labelText: isInsuranceExpired ? LocaleKeys.insuranceExpired.tr(context: context) : LocaleKeys.insuranceActive.tr(context: context), // labelText: isInsuranceExpired ? LocaleKeys.insuranceExpired.tr(context: context) : LocaleKeys.insuranceActive.tr(context: context),
@ -168,7 +169,7 @@ class PatientInsuranceCard extends StatelessWidget {
), ),
SizedBox(height: 10.h), SizedBox(height: 10.h),
isInsuranceExpired isInsuranceExpired
? CustomButton( ? isShowButtons ? CustomButton(
icon: AppAssets.update_insurance_card_icon, icon: AppAssets.update_insurance_card_icon,
iconColor: AppColors.warningColorYellow, iconColor: AppColors.warningColorYellow,
iconSize: 15.h, iconSize: 15.h,
@ -194,7 +195,7 @@ class PatientInsuranceCard extends StatelessWidget {
borderRadius: 12, borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h, height: 40.h,
) ) : SizedBox.shrink()
: Container(), : Container(),
], ],
), ),

@ -23,6 +23,7 @@ import 'package:hmg_patient_app_new/widgets/graph/custom_graph.dart';
import 'package:provider/provider.dart' show Consumer, Provider, ReadContext, Selector; import 'package:provider/provider.dart' show Consumer, Provider, ReadContext, Selector;
import '../../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight; import '../../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight;
import 'dart:ui' as ui;
class LabResultDetails extends StatelessWidget { class LabResultDetails extends StatelessWidget {
final LabResult recentLabResult; final LabResult recentLabResult;
@ -73,14 +74,14 @@ class LabResultDetails extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Row(
LocaleKeys.history.tr(context: context), mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: TextStyle( children: [
fontSize: 16.f, LocaleKeys.history.tr(context: context).toText16(isBold: true, color: AppColors.textColor),
fontFamily: 'Poppins', (labVM.isLabResultsHistoryShowMore ? "Show Less" : "Show More").toText14(weight: FontWeight.w500, color: AppColors.primaryRedColor, isUnderLine: true).onPress(() {
fontWeight: FontWeight.w600, labVM.setIsLabResultsHistoryShowMore();
color: AppColors.textColor, }),
), ],
), ),
SizedBox(height: 24.h,), SizedBox(height: 24.h,),
labHistoryList(labVM), labHistoryList(labVM),
@ -194,7 +195,7 @@ class LabResultDetails extends StatelessWidget {
], ],
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
("${LocaleKeys.resultOf.tr(context: context)} ${recentLabResult.verifiedOn ?? ""}").toText11(weight: FontWeight.w500, color: AppColors.greyTextColor), ("${LocaleKeys.resultOf.tr(context: context)} ${recentLabResult.verifiedOn ?? ""}").toText11(weight: FontWeight.w500, color: AppColors.greyTextColor, isEnglishOnly: true),
], ],
), ),
Row( Row(
@ -256,15 +257,16 @@ class LabResultDetails extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( (labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context)).toText16(isBold: true, color: AppColors.textColor),
labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context), // Text(
style: TextStyle( // labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context),
fontSize: 16.f, // style: TextStyle(
fontFamily: 'Poppins', // fontSize: 16.f,
fontWeight: FontWeight.w600, // fontFamily: 'Poppins',
color: AppColors.textColor, // fontWeight: FontWeight.w600,
), // color: AppColors.textColor,
), // ),
// ),
Row( Row(
spacing: 16.h, spacing: 16.h,
children: [ children: [
@ -335,6 +337,7 @@ class LabResultDetails extends StatelessWidget {
minY: labmodel.minY, minY: labmodel.minY,
maxX: labmodel.filteredGraphValues.length.toDouble() - .75, maxX: labmodel.filteredGraphValues.length.toDouble() - .75,
horizontalInterval: .1, horizontalInterval: .1,
isRTL: getIt.get<AppState>().isArabic(),
getDrawingHorizontalLine: (value) { getDrawingHorizontalLine: (value) {
value = double.parse(value.toStringAsFixed(1)); value = double.parse(value.toStringAsFixed(1));
if (value == labmodel.highRefrenceValue || value == labmodel.lowRefenceValue) { if (value == labmodel.highRefrenceValue || value == labmodel.lowRefenceValue) {
@ -416,10 +419,11 @@ class LabResultDetails extends StatelessWidget {
Widget labHistoryList(LabViewModel labmodel) { Widget labHistoryList(LabViewModel labmodel) {
return SizedBox( return SizedBox(
height: labmodel.filteredGraphValues.length < 3 ? labmodel.filteredGraphValues.length * 64 : 180.h, height: labmodel.filteredGraphValues.length < 3 ? labmodel.filteredGraphValues.length * 64 : (!labmodel.isLabResultsHistoryShowMore ? 180.h : 220.h),
child: ListView.separated( child: ListView.separated(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
itemCount: labmodel.filteredGraphValues.length, // itemCount: labmodel.isLabResultsHistoryShowMore ? (labmodel.filteredGraphValues.length < 3 ? labmodel.filteredGraphValues : 3) : labmodel.filteredGraphValues.length,
itemCount: !labmodel.isLabResultsHistoryShowMore ? (labmodel.filteredGraphValues.length < 3 ? labmodel.filteredGraphValues.length : 3) : labmodel.filteredGraphValues.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
var data = labmodel.filteredGraphValues.reversed.toList()[index]; var data = labmodel.filteredGraphValues.reversed.toList()[index];
return LabHistoryItem( return LabHistoryItem(

@ -93,7 +93,7 @@ class _SearchLabResultsContentState extends State<SearchLabResultsContent> {
), ),
SizedBox(height: ResponsiveExtension(20).h), SizedBox(height: ResponsiveExtension(20).h),
if (filteredSuggestions.isNotEmpty) ...[ if (filteredSuggestions.isNotEmpty) ...[
"Suggestions".toText16(isBold: true), "Suggestions".toText16(isBold: true, isEnglishOnly: true),
], ],
], ],
), ),
@ -147,6 +147,7 @@ class SuggestionChip extends StatelessWidget {
// color: isSelected ? AppColors.textColor : Colors.black87, // color: isSelected ? AppColors.textColor : Colors.black87,
color: AppColors.textColor, color: AppColors.textColor,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
isEnglishOnly: true
), ),
), ),
); );

@ -54,6 +54,7 @@ class NotificationsListPage extends StatelessWidget {
child: FadeInAnimation( child: FadeInAnimation(
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
notificationsVM.markAsRead(notificationID: notificationsVM.notificationsList[index].id ?? 0);
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(

@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/presentation/rate_appointment/rate_appointme
import 'package:hmg_patient_app_new/presentation/rate_appointment/widget/doctor_row.dart'; import 'package:hmg_patient_app_new/presentation/rate_appointment/widget/doctor_row.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class RateAppointmentDoctor extends StatefulWidget { class RateAppointmentDoctor extends StatefulWidget {
@ -48,16 +49,16 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
return Selector<AppointmentRatingViewModel, bool>( return Selector<AppointmentRatingViewModel, bool>(
selector: (_, vm) => vm.isRateClinic, selector: (_, vm) => vm.isRateClinic,
builder: (context, isRateClinic, child) => isRateClinic builder: (context, isRateClinic, child) =>
? RateAppointmentClinic(doctorNote: note, doctorRate: rating,) // isRateClinic ? RateAppointmentClinic(doctorNote: note, doctorRate: rating,)
: SizedBox( // :
height: sheetHeight, SizedBox(
height: sheetHeight,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Scrollable main content // Scrollable main content
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 0.0, left: 0, right: 0), padding: const EdgeInsets.only(top: 0.0, left: 0, right: 0),
child: Column( child: Column(
@ -162,7 +163,6 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
), ),
), ),
// Bottom action buttons pinned to bottom // Bottom action buttons pinned to bottom
SafeArea( SafeArea(
top: false, top: false,
@ -172,7 +172,7 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
children: [ children: [
Expanded( Expanded(
child: CustomButton( child: CustomButton(
text: "Later", text: LocaleKeys.later.tr(context: context),
backgroundColor: Color(0xffFEE9EA), backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B), textColor: Color(0xffED1C2B),
@ -184,12 +184,18 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
SizedBox(width: 10), SizedBox(width: 10),
Expanded( Expanded(
child: CustomButton( child: CustomButton(
text: LocaleKeys.next.tr(context: context), text: LocaleKeys.submit.tr(context: context),
onPressed: () { onPressed: () async {
// Set up clinic rating and show clinic rating view // Set up clinic rating and show clinic rating view
appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),); // appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),);
appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),); // appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),);
appointmentRatingViewModel!.setClinicOrDoctor(true); // appointmentRatingViewModel!.setClinicOrDoctor(true);
LoaderBottomSheet.showLoader();
await appointmentRatingViewModel!.submitDoctorRating(docRate: rating, docNote: note);
// await appointmentRatingViewModel!.submitClinicRating(clinicRate: rating, clinicNote: note);
LoaderBottomSheet.hideLoader();
Navigator.pop(context);
setState(() {}); setState(() {});
}, },

@ -31,6 +31,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
/// - [showBottomTitleDates]: Whether to show bottom axis labels. /// - [showBottomTitleDates]: Whether to show bottom axis labels.
/// - [isFullScreeGraph]: Whether the graph is fullscreen. /// - [isFullScreeGraph]: Whether the graph is fullscreen.
/// - [makeGraphBasedOnActualValue]: Use `actualValue` for plotting. /// - [makeGraphBasedOnActualValue]: Use `actualValue` for plotting.
/// - [isRTL]: Whether the graph should render in right-to-left direction.
/// ///
/// Example usage: /// Example usage:
/// ```dart /// ```dart
@ -70,6 +71,7 @@ class CustomGraph extends StatelessWidget {
final bool showLinePoints; final bool showLinePoints;
final double? cutOffY; final double? cutOffY;
final RangeAnnotations? rangeAnnotations; final RangeAnnotations? rangeAnnotations;
final bool isRTL;
///creates the left label and provide it to the chart as it will be used by other part of the application so the label will be different for every chart ///creates the left label and provide it to the chart as it will be used by other part of the application so the label will be different for every chart
final Widget Function(double) leftLabelFormatter; final Widget Function(double) leftLabelFormatter;
@ -113,7 +115,8 @@ class CustomGraph extends StatelessWidget {
this.showShadow = false, this.showShadow = false,
this.showLinePoints = false, this.showLinePoints = false,
this.cutOffY = 0, this.cutOffY = 0,
this.rangeAnnotations}); this.rangeAnnotations,
this.isRTL = false});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -122,6 +125,12 @@ class CustomGraph extends StatelessWidget {
final resolvedGraphShadowColor = graphShadowColor ?? AppColors.graphGridColor; final resolvedGraphShadowColor = graphShadowColor ?? AppColors.graphGridColor;
final resolvedGraphGridColor = graphGridColor ?? AppColors.graphGridColor; final resolvedGraphGridColor = graphGridColor ?? AppColors.graphGridColor;
// For RTL, reverse the data points so the graph reads right-to-left
final displayDataPoints = isRTL ? dataPoints.reversed.toList() : dataPoints;
final displaySecondaryDataPoints = (isRTL && secondaryDataPoints != null)
? secondaryDataPoints!.reversed.toList()
: secondaryDataPoints;
return Material( return Material(
color: AppColors.whiteColor, color: AppColors.whiteColor,
child: SizedBox( child: SizedBox(
@ -176,10 +185,10 @@ class CustomGraph extends StatelessWidget {
// Show tooltip for each touched line // Show tooltip for each touched line
return touchedSpots.map((spot) { return touchedSpots.map((spot) {
// Determine which dataset this spot belongs to // Determine which dataset this spot belongs to
final isSecondary = secondaryDataPoints != null && spot.barIndex == 1; final isSecondary = displaySecondaryDataPoints != null && spot.barIndex == 1;
final dataPoint = isSecondary final dataPoint = isSecondary
? secondaryDataPoints![spot.x.toInt()] ? displaySecondaryDataPoints[spot.x.toInt()]
: dataPoints[spot.x.toInt()]; : displayDataPoints[spot.x.toInt()];
return LineTooltipItem( return LineTooltipItem(
'${dataPoint.actualValue} ${dataPoint.unitOfMeasurement ?? ""} - ${dataPoint.displayTime}', '${dataPoint.actualValue} ${dataPoint.unitOfMeasurement ?? ""} - ${dataPoint.displayTime}',
@ -192,9 +201,19 @@ class CustomGraph extends StatelessWidget {
titlesData: FlTitlesData( titlesData: FlTitlesData(
leftTitles: AxisTitles( leftTitles: AxisTitles(
sideTitles: SideTitles( sideTitles: SideTitles(
showTitles: true, showTitles: !isRTL,
reservedSize: leftLabelReservedSize ?? 80,
interval: leftLabelInterval ?? .1,
getTitlesWidget: (value, _) {
return leftLabelFormatter(value);
},
),
),
rightTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: isRTL,
reservedSize: leftLabelReservedSize ?? 80, reservedSize: leftLabelReservedSize ?? 80,
interval: leftLabelInterval ?? .1, // Let fl_chart handle it interval: leftLabelInterval ?? .1,
getTitlesWidget: (value, _) { getTitlesWidget: (value, _) {
return leftLabelFormatter(value); return leftLabelFormatter(value);
}, },
@ -206,30 +225,28 @@ class CustomGraph extends StatelessWidget {
showTitles: showBottomTitleDates, showTitles: showBottomTitleDates,
reservedSize: bottomLabelReservedSize ?? 20, reservedSize: bottomLabelReservedSize ?? 20,
getTitlesWidget: (value, _) { getTitlesWidget: (value, _) {
return bottomLabelFormatter(value, dataPoints); return bottomLabelFormatter(value, displayDataPoints);
}, },
interval: 1, // ensures 1:1 mapping with spots interval: 1, // ensures 1:1 mapping with spots
), ),
), ),
topTitles: AxisTitles(), topTitles: AxisTitles(),
rightTitles: AxisTitles(),
), ),
borderData: FlBorderData( borderData: FlBorderData(
show: true, show: true,
border: const Border( border: Border(
bottom: BorderSide.none, bottom: BorderSide.none,
left: BorderSide(color: Colors.grey, width: .5), left: isRTL ? BorderSide.none : const BorderSide(color: Colors.grey, width: .5),
right: BorderSide.none, right: isRTL ? const BorderSide(color: Colors.grey, width: .5) : BorderSide.none,
top: BorderSide.none, top: BorderSide.none,
), ),
), ),
lineBarsData: _buildColoredLineSegments(dataPoints, showLinePoints, resolvedGraphColor, resolvedGraphShadowColor), lineBarsData: _buildColoredLineSegments(displayDataPoints, showLinePoints, resolvedGraphColor, resolvedGraphShadowColor,
secondaryPoints: displaySecondaryDataPoints),
gridData: FlGridData( gridData: FlGridData(
show: showGridLines ?? true, show: showGridLines ?? true,
drawVerticalLine: false, drawVerticalLine: false,
horizontalInterval: horizontalInterval, horizontalInterval: horizontalInterval,
// checkToShowHorizontalLine: (value) =>
// value >= 0 && value <= 100,
getDrawingHorizontalLine: getDrawingHorizontalLine ?? getDrawingHorizontalLine: getDrawingHorizontalLine ??
(value) { (value) {
return FlLine( return FlLine(
@ -245,7 +262,8 @@ class CustomGraph extends StatelessWidget {
); );
} }
List<LineChartBarData> _buildColoredLineSegments(List<DataPoint> dataPoints, bool showLinePoints, Color resolvedGraphColor, Color resolvedGraphShadowColor) { List<LineChartBarData> _buildColoredLineSegments(List<DataPoint> dataPoints, bool showLinePoints, Color resolvedGraphColor, Color resolvedGraphShadowColor,
{List<DataPoint>? secondaryPoints}) {
final List<FlSpot> allSpots = dataPoints.asMap().entries.map((entry) { final List<FlSpot> allSpots = dataPoints.asMap().entries.map((entry) {
double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value; double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value;
return FlSpot(entry.key.toDouble(), value); return FlSpot(entry.key.toDouble(), value);
@ -281,8 +299,8 @@ class CustomGraph extends StatelessWidget {
]; ];
// Add secondary line if provided (for dual-line graphs like blood pressure) // Add secondary line if provided (for dual-line graphs like blood pressure)
if (secondaryDataPoints != null && secondaryDataPoints!.isNotEmpty) { if (secondaryPoints != null && secondaryPoints.isNotEmpty) {
final List<FlSpot> secondarySpots = secondaryDataPoints!.asMap().entries.map((entry) { final List<FlSpot> secondarySpots = secondaryPoints.asMap().entries.map((entry) {
double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value; double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value;
return FlSpot(entry.key.toDouble(), value); return FlSpot(entry.key.toDouble(), value);
}).toList(); }).toList();

@ -5,6 +5,8 @@ import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -13,7 +15,7 @@ class LoaderBottomSheet {
static final NavigationService _navService = GetIt.I<NavigationService>(); static final NavigationService _navService = GetIt.I<NavigationService>();
static bool _isVisible = false; static bool _isVisible = false;
static void showLoader({String? loadingText}) { static void showLoader({String? loadingText, bool showCloseButton = false, Function? onCloseTap}) {
if (_isVisible) return; if (_isVisible) return;
_isVisible = true; _isVisible = true;
@ -32,7 +34,31 @@ class LoaderBottomSheet {
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)), borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
), ),
child: Center( child: (showCloseButton) ? Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"".toText20(),
if (showCloseButton)
IconButton(
icon: Icon(Icons.close, color: AppColors.textColor),
onPressed: () {
context.pop();
if(onCloseTap != null) {
onCloseTap();
}
},
)
else
SizedBox(width: 48), // Placeholder for alignment
],
),
Center(
child: Utils.getLoadingWidget(loadingText: loadingText),
).paddingSymmetrical(24.w, 0),
],
) : Center(
child: Utils.getLoadingWidget(loadingText: loadingText), child: Utils.getLoadingWidget(loadingText: loadingText),
).paddingSymmetrical(24.w, 0), ).paddingSymmetrical(24.w, 0),
); );

Loading…
Cancel
Save