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

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

@ -1612,28 +1612,28 @@
"sortByNearestLocation": "Sort by nearest to your location",
"giveLocationPermissionForNearestList": "Please grant location permission from app settings to see the nearest locations.",
"communicationLanguage": "Communication Language",
"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.",
"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.",
"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!",
"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.",
"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.",
"smartWatchServiceHeader": "Sync Your Smartwatch with Health Apps",
"smartWatchServiceDescription": "Seamlessly connect your smartwatch to our health app for effortless data tracking and personalized insights.",
"liveChatServiceHeader": "24/7 Expert\nAssistance",
"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",
"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.",
"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['PatientID'] = 3628599;
// body['PatientID'] = 1018977;
// body['PatientTypeID'] = 1;
// body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q";

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

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

@ -61,4 +61,14 @@ class LaserBodyPart {
data['CategoryNameN'] = this.categoryNameN;
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: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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
@ -87,6 +88,9 @@ class LabViewModel extends ChangeNotifier {
LabOrderResponseByAi? labOrderResponseByAi;
LabOrdersResponseByAi? labOrdersResponseByAi;
bool isLabAIAnalysisNeedsToBeShown = true;
bool isLabResultsHistoryShowMore = false;
LabViewModel({required this.labRepo, required this.errorHandlerService, required this.navigationService});
initLabProvider() {
@ -96,6 +100,8 @@ class LabViewModel extends ChangeNotifier {
labOrderTests.clear();
isLabOrdersLoading = true;
isLabResultsLoading = true;
isLabAIAnalysisNeedsToBeShown = true;
isLabResultsHistoryShowMore = false;
patientLabOrdersByClinic.clear();
patientLabOrdersByHospital.clear();
patientLabOrdersViewList.clear();
@ -114,6 +120,16 @@ class LabViewModel extends ChangeNotifier {
notifyListeners();
}
setIsLabAIAnalysisNeedsToBeShown(bool isLabAIAnalysisNeedsToBeShown) {
this.isLabAIAnalysisNeedsToBeShown = isLabAIAnalysisNeedsToBeShown;
notifyListeners();
}
setIsLabResultsHistoryShowMore() {
isLabResultsHistoryShowMore = !isLabResultsHistoryShowMore;
notifyListeners();
}
void setIsSortByClinic(bool value) {
isSortByClinic = value;
patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
@ -806,7 +822,13 @@ class LabViewModel extends ChangeNotifier {
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: loadingText);
// LoadingUtils.showFullScreenLoader(loadingText: loadingText);
LoaderBottomSheet.showLoader(
loadingText: loadingText,
showCloseButton: true,
onCloseTap: () {
setIsLabAIAnalysisNeedsToBeShown(false);
});
List<Map<String, dynamic>> results = [];
Map<String, dynamic> orderData = {"order_date": labOrder.orderDate ?? "", "clinic": labOrder.clinicDescription ?? "", "doctor": labOrder.doctorName ?? "", "results": []};
List<Map<String, dynamic>> testResults = [];
@ -829,16 +851,20 @@ class LabViewModel extends ChangeNotifier {
result.fold(
(failure) async {
LoadingUtils.hideFullScreenLoader();
await errorHandlerService.handleError(failure: failure);
if (isLabAIAnalysisNeedsToBeShown) {
await errorHandlerService.handleError(failure: failure);
}
},
(apiResponse) {
LoadingUtils.hideFullScreenLoader();
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
labOrdersResponseByAi = apiResponse.data;
navigationService.push(
MaterialPageRoute(builder: (_) => LabAiAnalysisDetailedPage()),
);
if (isLabAIAnalysisNeedsToBeShown) {
labOrdersResponseByAi = apiResponse.data;
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 {
LoaderBottomSheet.showLoader(loadingText: loadingText);
LoaderBottomSheet.showLoader(
loadingText: loadingText,
showCloseButton: true,
onCloseTap: () {
setIsLabAIAnalysisNeedsToBeShown(false);
});
List<Map<String, dynamic>> results = [];
results.add({
"Description": recentLabResult.description ?? '',
@ -857,19 +888,24 @@ class LabViewModel extends ChangeNotifier {
"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);
result.fold(
(failure) async {
LoaderBottomSheet.hideLoader();
await errorHandlerService.handleError(failure: failure);
if (isLabAIAnalysisNeedsToBeShown) {
await errorHandlerService.handleError(failure: failure);
}
},
(apiResponse) {
LoaderBottomSheet.hideLoader();
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
labOrderResponseByAi = apiResponse.data;
notifyListeners();
if (isLabAIAnalysisNeedsToBeShown) {
labOrderResponseByAi = apiResponse.data;
notifyListeners();
}
}
},
);

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

@ -12,6 +12,10 @@ abstract class NotificationsRepo {
required int pagingSize,
required int currentPage,
});
Future<Either<Failure, GenericApiModel<List<NotificationResponseModel>>>> markAsRead({
required int notificationID,
});
}
class NotificationsRepoImp implements NotificationsRepo {
@ -75,4 +79,38 @@ class NotificationsRepoImp implements NotificationsRepo {
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({
Function(dynamic)? onSuccess,
Function(String)? onError,

@ -1630,5 +1630,11 @@ abstract class LocaleKeys {
static const emergencyServiceDescription = 'emergencyServiceDescription';
static const homeHealthCareServiceHeader = 'homeHealthCareServiceHeader';
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() {
scheduleMicrotask(() async {
if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) {
CalenderUtilsNew calendarUtils = await CalenderUtilsNew.instance;
var doesExist = await calendarUtils.checkIfEventExist("${widget.patientAppointmentHistoryResponseModel.appointmentNo}");
myAppointmentsViewModel.setAppointmentReminder(doesExist, widget.patientAppointmentHistoryResponseModel);
setState(() {});
// CalenderUtilsNew calendarUtils = await CalenderUtilsNew.instance;
// var doesExist = await calendarUtils.checkIfEventExist("${widget.patientAppointmentHistoryResponseModel.appointmentNo}");
// myAppointmentsViewModel.setAppointmentReminder(doesExist, widget.patientAppointmentHistoryResponseModel);
// setState(() {});
}
});

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

@ -276,7 +276,46 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
// Show empty state or actual list
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(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -671,7 +710,7 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
return SizedBox.shrink();
}
void _handleSortByLocationToggle(bool value, AppointmentViaRegionViewmodel regionVM) {
void _handleSortByLocationToggle(bool value, AppointmentViaRegionViewmodel regionVM) {
if (value) {
final locationUtils = getIt.get<LocationUtils>();
locationUtils.getLocation(

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

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

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

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

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

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

@ -20,10 +20,11 @@ import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
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;
bool isInsuranceExpired = false;
bool isShowButtons = true;
late InsuranceViewModel insuranceViewModel;
late AppState appState;
@ -60,7 +61,7 @@ class PatientInsuranceCard extends StatelessWidget {
),
],
),
AppCustomChipWidget(
isShowButtons ? AppCustomChipWidget(
icon: isCurrentPatientInsuranceExpired(insuranceCardDetailsModel.cardValidTo!)
? AppAssets.cancel_circle_icon
: insuranceViewModel.isInsuranceActive
@ -120,7 +121,7 @@ class PatientInsuranceCard extends StatelessWidget {
? AppColors.successColor.withOpacity(0.1)
: AppColors.warningColorYellow.withOpacity(0.1),
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: insuranceViewModel.isInsuranceActive ? 6.w : 0.w),
).toShimmer2(isShow: insuranceViewModel.isInsuranceLoading),
).toShimmer2(isShow: insuranceViewModel.isInsuranceLoading) : SizedBox.shrink(),
// AppCustomChipWidget(
// icon: isInsuranceExpired ? AppAssets.cancel_circle_icon : AppAssets.insurance_active_icon,
// labelText: isInsuranceExpired ? LocaleKeys.insuranceExpired.tr(context: context) : LocaleKeys.insuranceActive.tr(context: context),
@ -168,7 +169,7 @@ class PatientInsuranceCard extends StatelessWidget {
),
SizedBox(height: 10.h),
isInsuranceExpired
? CustomButton(
? isShowButtons ? CustomButton(
icon: AppAssets.update_insurance_card_icon,
iconColor: AppColors.warningColorYellow,
iconSize: 15.h,
@ -194,7 +195,7 @@ class PatientInsuranceCard extends StatelessWidget {
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
)
) : SizedBox.shrink()
: 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 '../../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight;
import 'dart:ui' as ui;
class LabResultDetails extends StatelessWidget {
final LabResult recentLabResult;
@ -73,14 +74,14 @@ class LabResultDetails extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
LocaleKeys.history.tr(context: context),
style: TextStyle(
fontSize: 16.f,
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
color: AppColors.textColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.history.tr(context: context).toText16(isBold: true, color: AppColors.textColor),
(labVM.isLabResultsHistoryShowMore ? "Show Less" : "Show More").toText14(weight: FontWeight.w500, color: AppColors.primaryRedColor, isUnderLine: true).onPress(() {
labVM.setIsLabResultsHistoryShowMore();
}),
],
),
SizedBox(height: 24.h,),
labHistoryList(labVM),
@ -194,7 +195,7 @@ class LabResultDetails extends StatelessWidget {
],
),
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(
@ -256,15 +257,16 @@ class LabResultDetails extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context),
style: TextStyle(
fontSize: 16.f,
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
color: AppColors.textColor,
),
),
(labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context)).toText16(isBold: true, color: AppColors.textColor),
// Text(
// labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context),
// style: TextStyle(
// fontSize: 16.f,
// fontFamily: 'Poppins',
// fontWeight: FontWeight.w600,
// color: AppColors.textColor,
// ),
// ),
Row(
spacing: 16.h,
children: [
@ -335,6 +337,7 @@ class LabResultDetails extends StatelessWidget {
minY: labmodel.minY,
maxX: labmodel.filteredGraphValues.length.toDouble() - .75,
horizontalInterval: .1,
isRTL: getIt.get<AppState>().isArabic(),
getDrawingHorizontalLine: (value) {
value = double.parse(value.toStringAsFixed(1));
if (value == labmodel.highRefrenceValue || value == labmodel.lowRefenceValue) {
@ -416,10 +419,11 @@ class LabResultDetails extends StatelessWidget {
Widget labHistoryList(LabViewModel labmodel) {
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(
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) {
var data = labmodel.filteredGraphValues.reversed.toList()[index];
return LabHistoryItem(

@ -93,7 +93,7 @@ class _SearchLabResultsContentState extends State<SearchLabResultsContent> {
),
SizedBox(height: ResponsiveExtension(20).h),
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: AppColors.textColor,
fontWeight: FontWeight.w500,
isEnglishOnly: true
),
),
);

@ -54,6 +54,7 @@ class NotificationsListPage extends StatelessWidget {
child: FadeInAnimation(
child: GestureDetector(
onTap: () {
notificationsVM.markAsRead(notificationID: notificationsVM.notificationsList[index].id ?? 0);
Navigator.push(
context,
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/theme/colors.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';
class RateAppointmentDoctor extends StatefulWidget {
@ -48,16 +49,16 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
return Selector<AppointmentRatingViewModel, bool>(
selector: (_, vm) => vm.isRateClinic,
builder: (context, isRateClinic, child) => isRateClinic
? RateAppointmentClinic(doctorNote: note, doctorRate: rating,)
: SizedBox(
height: sheetHeight,
builder: (context, isRateClinic, child) =>
// isRateClinic ? RateAppointmentClinic(doctorNote: note, doctorRate: rating,)
// :
SizedBox(
height: sheetHeight,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Scrollable main content
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 0.0, left: 0, right: 0),
child: Column(
@ -162,7 +163,6 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
),
),
// Bottom action buttons pinned to bottom
SafeArea(
top: false,
@ -172,7 +172,7 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
children: [
Expanded(
child: CustomButton(
text: "Later",
text: LocaleKeys.later.tr(context: context),
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
@ -184,12 +184,18 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
SizedBox(width: 10),
Expanded(
child: CustomButton(
text: LocaleKeys.next.tr(context: context),
onPressed: () {
text: LocaleKeys.submit.tr(context: context),
onPressed: () async {
// Set up clinic rating and show clinic rating view
appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),);
appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),);
appointmentRatingViewModel!.setClinicOrDoctor(true);
// appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),);
// appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),);
// 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(() {});
},

@ -31,6 +31,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
/// - [showBottomTitleDates]: Whether to show bottom axis labels.
/// - [isFullScreeGraph]: Whether the graph is fullscreen.
/// - [makeGraphBasedOnActualValue]: Use `actualValue` for plotting.
/// - [isRTL]: Whether the graph should render in right-to-left direction.
///
/// Example usage:
/// ```dart
@ -70,6 +71,7 @@ class CustomGraph extends StatelessWidget {
final bool showLinePoints;
final double? cutOffY;
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
final Widget Function(double) leftLabelFormatter;
@ -113,7 +115,8 @@ class CustomGraph extends StatelessWidget {
this.showShadow = false,
this.showLinePoints = false,
this.cutOffY = 0,
this.rangeAnnotations});
this.rangeAnnotations,
this.isRTL = false});
@override
Widget build(BuildContext context) {
@ -122,6 +125,12 @@ class CustomGraph extends StatelessWidget {
final resolvedGraphShadowColor = graphShadowColor ?? 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(
color: AppColors.whiteColor,
child: SizedBox(
@ -176,10 +185,10 @@ class CustomGraph extends StatelessWidget {
// Show tooltip for each touched line
return touchedSpots.map((spot) {
// Determine which dataset this spot belongs to
final isSecondary = secondaryDataPoints != null && spot.barIndex == 1;
final isSecondary = displaySecondaryDataPoints != null && spot.barIndex == 1;
final dataPoint = isSecondary
? secondaryDataPoints![spot.x.toInt()]
: dataPoints[spot.x.toInt()];
? displaySecondaryDataPoints[spot.x.toInt()]
: displayDataPoints[spot.x.toInt()];
return LineTooltipItem(
'${dataPoint.actualValue} ${dataPoint.unitOfMeasurement ?? ""} - ${dataPoint.displayTime}',
@ -192,9 +201,19 @@ class CustomGraph extends StatelessWidget {
titlesData: FlTitlesData(
leftTitles: AxisTitles(
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,
interval: leftLabelInterval ?? .1, // Let fl_chart handle it
interval: leftLabelInterval ?? .1,
getTitlesWidget: (value, _) {
return leftLabelFormatter(value);
},
@ -206,30 +225,28 @@ class CustomGraph extends StatelessWidget {
showTitles: showBottomTitleDates,
reservedSize: bottomLabelReservedSize ?? 20,
getTitlesWidget: (value, _) {
return bottomLabelFormatter(value, dataPoints);
return bottomLabelFormatter(value, displayDataPoints);
},
interval: 1, // ensures 1:1 mapping with spots
),
),
topTitles: AxisTitles(),
rightTitles: AxisTitles(),
),
borderData: FlBorderData(
show: true,
border: const Border(
border: Border(
bottom: BorderSide.none,
left: BorderSide(color: Colors.grey, width: .5),
right: BorderSide.none,
left: isRTL ? BorderSide.none : const BorderSide(color: Colors.grey, width: .5),
right: isRTL ? const BorderSide(color: Colors.grey, width: .5) : BorderSide.none,
top: BorderSide.none,
),
),
lineBarsData: _buildColoredLineSegments(dataPoints, showLinePoints, resolvedGraphColor, resolvedGraphShadowColor),
lineBarsData: _buildColoredLineSegments(displayDataPoints, showLinePoints, resolvedGraphColor, resolvedGraphShadowColor,
secondaryPoints: displaySecondaryDataPoints),
gridData: FlGridData(
show: showGridLines ?? true,
drawVerticalLine: false,
horizontalInterval: horizontalInterval,
// checkToShowHorizontalLine: (value) =>
// value >= 0 && value <= 100,
getDrawingHorizontalLine: getDrawingHorizontalLine ??
(value) {
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) {
double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.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)
if (secondaryDataPoints != null && secondaryDataPoints!.isNotEmpty) {
final List<FlSpot> secondarySpots = secondaryDataPoints!.asMap().entries.map((entry) {
if (secondaryPoints != null && secondaryPoints.isNotEmpty) {
final List<FlSpot> secondarySpots = secondaryPoints.asMap().entries.map((entry) {
double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value;
return FlSpot(entry.key.toDouble(), value);
}).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/utils/size_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/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@ -13,7 +15,7 @@ class LoaderBottomSheet {
static final NavigationService _navService = GetIt.I<NavigationService>();
static bool _isVisible = false;
static void showLoader({String? loadingText}) {
static void showLoader({String? loadingText, bool showCloseButton = false, Function? onCloseTap}) {
if (_isVisible) return;
_isVisible = true;
@ -32,7 +34,31 @@ class LoaderBottomSheet {
color: AppColors.whiteColor,
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),
).paddingSymmetrical(24.w, 0),
);

Loading…
Cancel
Save