From 8472a455307e2e1cef0172e2812f46d85b7c149e Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Thu, 29 Jan 2026 15:24:27 +0300 Subject: [PATCH 1/5] updates --- lib/core/api_consts.dart | 2 +- .../appointment_details_page.dart | 15 ++++---- .../widgets/appointment_card.dart | 36 ++++++++++++------- .../widgets/appointment_doctor_card.dart | 8 +++-- .../health_calculators_page.dart | 3 +- .../widgets/ancillary_orders_list.dart | 19 ++++------ 6 files changed, 46 insertions(+), 37 deletions(-) diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index bec908b..8319c9d 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -680,7 +680,7 @@ const DASHBOARD = 'Services/Patients.svc/REST/PatientDashboard'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 46ed33e..daf5b61 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -15,6 +15,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart'; +import 'package:hmg_patient_app_new/features/contact_us/models/feedback_type.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; @@ -92,6 +93,7 @@ class _AppointmentDetailsPageState extends State { title: LocaleKeys.appointmentDetails.tr(context: context), report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? () { + contactUsViewModel.setSelectedFeedbackType(FeedbackType(id: 1, nameEN: "Complaint for appointment", nameAR: 'شكوى على موعد')); contactUsViewModel.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel); Navigator.of(context).push( CustomPageRoute( @@ -216,9 +218,6 @@ class _AppointmentDetailsPageState extends State { child: Image.network( "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}", fit: BoxFit.contain, - // errorBuilder: (cxt, child, tr) { - // return SizedBox.shrink(); - // }, ), ), Positioned( @@ -756,9 +755,10 @@ class _AppointmentDetailsPageState extends State { ], ).paddingOnly(left: 16.h, top: 24.h, right: 16.h, bottom: 0.h), AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) - ? CustomButton( - text: LocaleKeys.rebookAppointment.tr(context: context), - onPressed: () { + ? !widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! + ? CustomButton( + text: LocaleKeys.rebookAppointment.tr(context: context), + onPressed: () { openDoctorScheduleCalendar(); }, backgroundColor: AppColors.successColor, @@ -772,7 +772,8 @@ class _AppointmentDetailsPageState extends State { icon: AppAssets.add_icon, iconColor: AppColors.whiteColor, iconSize: 18.h, - ).paddingSymmetrical(16.h, 24.h) + ).paddingSymmetrical(16.h, 24.h) + : SizedBox.shrink() : CustomButton( text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), onPressed: () { diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index aa82b51..a8735d0 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -153,7 +153,7 @@ class AppointmentCard extends StatelessWidget { SizedBox(height: 2.h), (isFoldable || isTablet) ? "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor) - : "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor), + : "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(isBold: true, color: AppColors.textColor), ], ), ).circle(100).toShimmer2(isShow: isLoading), @@ -228,18 +228,27 @@ class AppointmentCard extends StatelessWidget { Widget _buildActionArea(BuildContext context, AppState appState) { if ((patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) && AppointmentType.isArrived(patientAppointmentHistoryResponseModel)) { return CustomButton( - text: LocaleKeys.viewDetails.tr(context: context), + text: isFromMedicalReport ? LocaleKeys.selectAppointment.tr(context: context) : LocaleKeys.viewDetails.tr(context: context), onPressed: () { - Navigator.of(context) - .push( - CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel), - ), - ) - .then((_) { - myAppointmentsViewModel.initAppointmentsViewModel(); - myAppointmentsViewModel.getPatientAppointments(true, false); - }); + if (isFromMedicalReport) { + if (isForFeedback) { + contactUsViewModel!.setPatientFeedbackSelectedAppointment(patientAppointmentHistoryResponseModel); + } else { + medicalFileViewModel!.setSelectedMedicalReportAppointment(patientAppointmentHistoryResponseModel); + } + Navigator.pop(context, false); + } else { + Navigator.of(context) + .push( + CustomPageRoute( + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel), + ), + ) + .then((_) { + myAppointmentsViewModel.initAppointmentsViewModel(); + myAppointmentsViewModel.getPatientAppointments(true, false); + }); + } }, backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, @@ -250,6 +259,9 @@ class AppointmentCard extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: 10.w), // height: isTablet || isFoldable ? 46.h : 40.h, height: 40.h, + icon: isFromMedicalReport ? AppAssets.checkmark_icon : null, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, ); } else { if (isFromMedicalReport) { diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index 8583d9b..6eaa379 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -159,8 +159,9 @@ class AppointmentDoctorCard extends StatelessWidget { icon: AppAssets.ask_doctor_icon, iconColor: AppColors.primaryRedColor, ) - : CustomButton( - text: LocaleKeys.rebookSameDoctor.tr(), + : !patientAppointmentHistoryResponseModel.isLiveCareAppointment! + ? CustomButton( + text: LocaleKeys.rebookSameDoctor.tr(), onPressed: () { onRescheduleTap(); }, @@ -175,7 +176,8 @@ class AppointmentDoctorCard extends StatelessWidget { icon: AppAssets.rebook_appointment_icon, iconColor: AppColors.blackColor, iconSize: 14.h, - ); + ) + : SizedBox.shrink(); } else { return patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false ? CustomButton( diff --git a/lib/presentation/health_calculators_and_converts/health_calculators_page.dart b/lib/presentation/health_calculators_and_converts/health_calculators_page.dart index 95e9aec..aae25b0 100644 --- a/lib/presentation/health_calculators_and_converts/health_calculators_page.dart +++ b/lib/presentation/health_calculators_and_converts/health_calculators_page.dart @@ -204,7 +204,7 @@ class _HealthCalculatorsPageState extends State { crossAxisCount: 3, // 4 icons per row crossAxisSpacing: 16.w, mainAxisSpacing: 16.w, - childAspectRatio: 0.80), + childAspectRatio: 0.85), physics: NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: type == HealthCalculatorEnum.general ? generalHealthServices.length : womenHealthServices.length, @@ -214,6 +214,7 @@ class _HealthCalculatorsPageState extends State { icon: type == HealthCalculatorEnum.general ? generalHealthServices[index].icon : womenHealthServices[index].icon, labelText: type == HealthCalculatorEnum.general ? generalHealthServices[index].title : womenHealthServices[index].title, onTap: () { + Navigator.pop(context); Navigator.of(context).push( CustomPageRoute( page: HealthCalculatorDetailedPage( diff --git a/lib/presentation/todo_section/widgets/ancillary_orders_list.dart b/lib/presentation/todo_section/widgets/ancillary_orders_list.dart index 05eb6c7..58ee6d4 100644 --- a/lib/presentation/todo_section/widgets/ancillary_orders_list.dart +++ b/lib/presentation/todo_section/widgets/ancillary_orders_list.dart @@ -71,19 +71,12 @@ class AncillaryOrdersList extends StatelessWidget { return Center( child: Padding( padding: EdgeInsets.symmetric(vertical: 40.h), - child: Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 12.r, - hasShadow: false, - ), - child: Utils.getNoDataWidget( - context, - noDataText: LocaleKeys.youDontHaveAnyAncillaryOrdersYet.tr(context: context), - isSmallWidget: true, - width: 62.w, - height: 62.h, - ), + child: Utils.getNoDataWidget( + context, + noDataText: LocaleKeys.youDontHaveAnyAncillaryOrdersYet.tr(context: context), + isSmallWidget: true, + width: 62.w, + height: 62.h, ), ), ); From bafe835ebd759d3a0e3005e84e4459d5168bba0b Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Thu, 29 Jan 2026 16:12:19 +0300 Subject: [PATCH 2/5] Scheduled livecare appointment booking fixes --- .../book_appointments_view_model.dart | 12 +++- .../appointment_details_page.dart | 2 +- .../book_appointment/select_clinic_page.dart | 2 +- .../book_appointment/select_doctor_page.dart | 56 ++++++++++++++++++- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index 664f244..ac692b1 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -340,6 +340,14 @@ class BookAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } + setIsLiveCareDoctorsListLoading(bool value) { + if (value) { + liveCareDoctorsList.clear(); + } + isDoctorsListLoading = value; + notifyListeners(); + } + setIsClinicsListLoading(bool value) { if (value) { clinicsList.clear(); @@ -467,10 +475,10 @@ class BookAppointmentsViewModel extends ChangeNotifier { } Future getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async { - doctorsList.clear(); + liveCareDoctorsList.clear(); + notifyListeners(); final result = await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError); - result.fold( (failure) async { onError!(LocaleKeys.noDoctorFound.tr()); diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index daf5b61..e93d604 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -216,7 +216,7 @@ class _AppointmentDetailsPageState extends State { borderRadius: BorderRadius.circular(24.r), // Todo: what is this???? Api Key??? 😲 child: Image.network( - "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}", + "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x${(MediaQuery.of(context).size.height * 0.35).toInt()}&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}", fit: BoxFit.contain, ), ), diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index d96c9da..ac40d69 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -941,7 +941,7 @@ class _SelectClinicPageState extends State { void onLiveCareClinicSelected(GetLiveCareClinicsResponseModel clinic) { bookAppointmentsViewModel.setLiveCareSelectedClinic(clinic); - bookAppointmentsViewModel.setIsDoctorsListLoading(true); + bookAppointmentsViewModel.setIsLiveCareDoctorsListLoading(true); Navigator.of(context).push( CustomPageRoute( page: SelectDoctorPage(), diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart index 3723bee..f8a0ad9 100644 --- a/lib/presentation/book_appointment/select_doctor_page.dart +++ b/lib/presentation/book_appointment/select_doctor_page.dart @@ -95,9 +95,59 @@ class _SelectDoctorPageState extends State { child: Padding( padding: EdgeInsets.symmetric(horizontal: 24.h), child: Consumer(builder: (context, bookAppointmentsVM, child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ + return bookAppointmentsViewModel.isLiveCareSchedule + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.separated( + padding: EdgeInsets.only(top: 16.h), + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : (bookAppointmentsVM.liveCareDoctorsList.isNotEmpty ? bookAppointmentsVM.liveCareDoctorsList.length : 1), + itemBuilder: (context, index) { + // final isExpanded = bookAppointmentsVM.expandedGroupIndex == index; + final isExpanded = true; + return bookAppointmentsVM.isDoctorsListLoading + ? DoctorCard( + doctorsListResponseModel: DoctorsListResponseModel(), + isLoading: true, + bookAppointmentsViewModel: bookAppointmentsViewModel, + ) + : bookAppointmentsVM.liveCareDoctorsList.isEmpty + ? Utils.getNoDataWidget(context, noDataText: LocaleKeys.noDoctorFound.tr()) + : AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + child: Padding( + padding: EdgeInsets.all(16.h), + child: DoctorCard( + isLoading: bookAppointmentsVM.isClinicsListLoading, + doctorsListResponseModel: bookAppointmentsVM.liveCareDoctorsList[index], + bookAppointmentsViewModel: bookAppointmentsViewModel, + ).onPress(() { + // onLiveCareClinicSelected(bookAppointmentsVM.liveCareClinicsList[index]); + }), + ), + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), + ), + SizedBox(height: 24.h), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ SizedBox(height: 16.h), Row( spacing: 8.h, From 5c684a6a0de1346b64b1fd6280c1d7412a69b07e Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Mon, 2 Feb 2026 13:06:09 +0300 Subject: [PATCH 3/5] Doctor Rating details implemented --- assets/langs/ar-SA.json | 4 - assets/langs/en-US.json | 6 +- lib/core/api/api_client.dart | 1 - lib/core/api_consts.dart | 2 +- .../authentication_view_model.dart | 2 +- .../book_appointments_repo.dart | 48 +++++ .../book_appointments_view_model.dart | 44 ++++ .../doctor_rating_details_response_model.dart | 36 ++++ .../medical_file/medical_file_view_model.dart | 10 +- .../prescriptions_view_model.dart | 23 ++- .../todo_section/todo_section_view_model.dart | 23 ++- .../book_appointment/doctor_profile_page.dart | 36 +++- .../book_appointment/widgets/doctor_card.dart | 53 ++--- .../widgets/doctor_rating_details.dart | 195 ++++++++++++++++++ .../er_online_checkin_home.dart | 9 + .../patient_sickleaves_list_page.dart | 49 ++--- .../monthly_report/monthly_report.dart | 5 +- .../prescription_detail_page.dart | 11 +- .../prescriptions/prescription_item_view.dart | 81 ++------ .../prescriptions_list_page.dart | 33 +-- .../ancillary_procedures_details_page.dart | 1 + 21 files changed, 485 insertions(+), 187 deletions(-) create mode 100644 lib/features/book_appointments/models/resp_models/doctor_rating_details_response_model.dart create mode 100644 lib/presentation/book_appointment/widgets/doctor_rating_details.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index d64b9b7..cb2b71a 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -1496,10 +1496,6 @@ "someRemarksAboutPrescription": "ستجدون هنا بعض الملاحظات حول الوصفة الطبية", "notifyMeBeforeConsumptionTime": "أبلغني قبل وقت الاستهلاك", "noMedicationsToday": "لا أدوية اليوم", - "route": "Route: {route}", - "frequency": "Frequency: {frequency}", - "instruction": "Instruction: {instruction}", - "duration": "Duration: {days}", "reminders": "تذكيرات", "reminderAddedToCalendar": "تمت إضافة تذكير إلى التقويم ✅", "errorWhileSettingCalendar": "حدث خطأ أثناء ضبط التقويم:{error}", diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 03b766d..e56d861 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -1487,10 +1487,8 @@ "someRemarksAboutPrescription": "some remarks about the prescription will be here", "notifyMeBeforeConsumptionTime": "Notify me before the consumption time", "noMedicationsToday": "No medications today", - "route": "Route: {route}", - "frequency": "Frequency: {frequency}", - "instruction": "Instruction: {instruction}", - "duration": "Duration: {days}", + "route": "Route", + "frequency": "Frequency", "reminders": "Reminders", "reminderAddedToCalendar": "Reminder added to calendar ✅", "errorWhileSettingCalendar": "Error while setting calendar: {error}", diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index c063932..3eb8fa2 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -184,7 +184,6 @@ class ApiClientImp implements ApiClient { // body['TokenID'] = "@dm!n"; // body['PatientID'] = 4769038; // body['PatientTypeID'] = 1; - // // body['PatientOutSA'] = 0; // body['SessionID'] = "45786230487560q"; } diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 8319c9d..bec908b 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -680,7 +680,7 @@ const DASHBOARD = 'Services/Patients.svc/REST/PatientDashboard'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 8206160..c49554f 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -605,7 +605,7 @@ class AuthenticationViewModel extends ChangeNotifier { } // _appState.setUserBloodGroup = (activation.patientBlodType ?? ""); _appState.setAppAuthToken = activation.authenticationTokenId; - myAppointmentsVM.getActiveAppointmentsCount(); + // myAppointmentsVM.getActiveAppointmentsCount(); final request = RequestUtils.getAuthanticatedCommonRequest().toJson(); bool isUserAgreedBefore = await checkIfUserAgreedBefore(request: request); diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart index cfd473e..cab79ad 100644 --- a/lib/features/book_appointments/book_appointments_repo.dart +++ b/lib/features/book_appointments/book_appointments_repo.dart @@ -30,6 +30,8 @@ abstract class BookAppointmentsRepo { Future>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}); + Future>> getDoctorRatingDetails(int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}); + Future>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}); @@ -298,6 +300,52 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo { } } + @override + Future>> getDoctorRatingDetails(int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}) async { + Map mapDevice = { + "DoctorID": doctorId, + "PatientID": 0, + "License": true, + "IsRegistered": true, + "isDentalAllowedBackend": false, + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + GET_DOCTOR_RATING_DETAILS, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + if (onError != null) { + onError(error); + } + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + if (onSuccess != null) { + onSuccess(response); + } + } 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())); + } + } + //TODO: Implement the logic for Dental & laser clinics @override Future>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index ac692b1..34858b4 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -17,6 +17,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot. import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/appointment_nearest_gate_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_rating_details_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart'; @@ -92,6 +93,9 @@ class BookAppointmentsViewModel extends ChangeNotifier { late DoctorsProfileResponseModel doctorsProfileResponseModel; + bool isDoctorRatingDetailsLoading = false; + List doctorDetailsList = []; + List slotsList = []; List docFreeSlots = []; List dayEvents = []; @@ -622,6 +626,46 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } + Future getDoctorRatingDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async { + isDoctorRatingDetailsLoading = true; + doctorDetailsList.clear(); + notifyListeners(); + + final result = await bookAppointmentsRepo.getDoctorRatingDetails(selectedDoctor.doctorID ?? 0, onSuccess: onSuccess, onError: onError); + + result.fold( + (failure) async { + if (onError != null) { + onError("Failed to load doctor rating details"); + } + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + if (onError != null) { + onError(apiResponse.errorMessage ?? "Unknown error occurred"); + } + isDoctorRatingDetailsLoading = false; + } else if (apiResponse.messageStatus == 1) { + try { + apiResponse.data['DoctorRatingDetailsList'].forEach((v) { + doctorDetailsList.add(DoctorRateDetails.fromJson(v)); + }); + isDoctorRatingDetailsLoading = false; + // doctorRatingDetails = DoctorRateDetails.fromJson(apiResponse.data); + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse.data); + } + } catch (e) { + if (onError != null) { + onError("Failed to parse rating details: ${e.toString()}"); + } + } + } + }, + ); + } + Future getDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { docFreeSlots.clear(); DateTime date; diff --git a/lib/features/book_appointments/models/resp_models/doctor_rating_details_response_model.dart b/lib/features/book_appointments/models/resp_models/doctor_rating_details_response_model.dart new file mode 100644 index 0000000..5866885 --- /dev/null +++ b/lib/features/book_appointments/models/resp_models/doctor_rating_details_response_model.dart @@ -0,0 +1,36 @@ +class DoctorRateDetails { + dynamic doctorID; + dynamic projectID; + dynamic clinicID; + dynamic rate; + dynamic patientNumber; + dynamic ratio; + + DoctorRateDetails( + {this.doctorID, + this.projectID, + this.clinicID, + this.rate, + this.patientNumber, + this.ratio}); + + DoctorRateDetails.fromJson(Map json) { + doctorID = json['DoctorID']; + projectID = json['ProjectID']; + clinicID = json['ClinicID']; + rate = json['Rate']; + patientNumber = json['PatientNumber']; + ratio = json['Ratio']; + } + + Map toJson() { + final Map data = new Map(); + data['DoctorID'] = this.doctorID; + data['ProjectID'] = this.projectID; + data['ClinicID'] = this.clinicID; + data['Rate'] = this.rate; + data['PatientNumber'] = this.patientNumber; + data['Ratio'] = this.ratio; + return data; + } +} diff --git a/lib/features/medical_file/medical_file_view_model.dart b/lib/features/medical_file/medical_file_view_model.dart index 17cbb94..b26a87a 100644 --- a/lib/features/medical_file/medical_file_view_model.dart +++ b/lib/features/medical_file/medical_file_view_model.dart @@ -41,6 +41,7 @@ class MedicalFileViewModel extends ChangeNotifier { List patientSickLeavesViewList = []; bool isSickLeavesSortByClinic = true; + bool isSickLeavesDataNeedsReloading = true; List patientAllergiesList = []; @@ -163,15 +164,15 @@ class MedicalFileViewModel extends ChangeNotifier { } setIsPatientSickLeaveListLoading(bool val) { - if (val) { + if (val && isSickLeavesDataNeedsReloading) { patientSickLeaveList.clear(); patientSickLeavesByClinic.clear(); patientSickLeavesByHospital.clear(); patientSickLeavesViewList.clear(); patientSickLeavePDFBase64 = ""; isSickLeavesSortByClinic = true; + isPatientSickLeaveListLoading = val; } - isPatientSickLeaveListLoading = val; notifyListeners(); } @@ -267,6 +268,10 @@ class MedicalFileViewModel extends ChangeNotifier { } Future getPatientSickLeaveList({Function(dynamic)? onSuccess, Function(String)? onError}) async { + if (!isSickLeavesDataNeedsReloading) { + return; + } + patientSickLeaveList.clear(); final result = await medicalFileRepo.getPatientSickLeavesList(); @@ -309,6 +314,7 @@ class MedicalFileViewModel extends ChangeNotifier { } } patientSickLeavesViewList = patientSickLeavesByClinic; + isSickLeavesDataNeedsReloading = false; notifyListeners(); if (onSuccess != null) { diff --git a/lib/features/prescriptions/prescriptions_view_model.dart b/lib/features/prescriptions/prescriptions_view_model.dart index 3f9af34..a7cd42f 100644 --- a/lib/features/prescriptions/prescriptions_view_model.dart +++ b/lib/features/prescriptions/prescriptions_view_model.dart @@ -47,20 +47,24 @@ class PrescriptionsViewModel extends ChangeNotifier { late GeocodeResponse locationGeocodeResponse; bool isPrescriptionsDeliveryOrdersLoading = false; + + bool isPrescriptionsDataNeedsReloading = true; List prescriptionsOrderList = []; PrescriptionsViewModel({required this.prescriptionsRepo, required this.errorHandlerService, required this.navServices}); initPrescriptionsViewModel() { - patientPrescriptionOrders.clear(); - patientPrescriptionOrdersByClinic.clear(); - patientPrescriptionOrdersByHospital.clear(); - patientPrescriptionOrdersViewList.clear(); - prescriptionsOrderList.clear(); - isPrescriptionsOrdersLoading = true; + if (isPrescriptionsDataNeedsReloading) { + patientPrescriptionOrders.clear(); + patientPrescriptionOrdersByClinic.clear(); + patientPrescriptionOrdersByHospital.clear(); + patientPrescriptionOrdersViewList.clear(); + prescriptionsOrderList.clear(); + isPrescriptionsOrdersLoading = true; + getPatientPrescriptionOrders(); + } isSortByClinic = true; isPrescriptionsDeliveryOrdersLoading = true; - getPatientPrescriptionOrders(); notifyListeners(); } @@ -98,6 +102,10 @@ class PrescriptionsViewModel extends ChangeNotifier { } Future getPatientPrescriptionOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { + if (!isPrescriptionsDataNeedsReloading) { + return; + } + final result = await prescriptionsRepo.getPatientPrescriptionOrders(patientId: "1231755"); result.fold( @@ -131,6 +139,7 @@ class PrescriptionsViewModel extends ChangeNotifier { } } patientPrescriptionOrdersViewList = patientPrescriptionOrdersByClinic; + isPrescriptionsDataNeedsReloading = false; notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); diff --git a/lib/features/todo_section/todo_section_view_model.dart b/lib/features/todo_section/todo_section_view_model.dart index 92d893a..0f6cc87 100644 --- a/lib/features/todo_section/todo_section_view_model.dart +++ b/lib/features/todo_section/todo_section_view_model.dart @@ -13,17 +13,20 @@ class TodoSectionViewModel extends ChangeNotifier { String? notificationsCount = "0"; initializeTodoSectionViewModel() async { - patientAncillaryOrdersList.clear(); - isAncillaryOrdersLoading = true; - isAncillaryDetailsProceduresLoading = true; - notificationsCount = "0"; - getPatientOnlineAncillaryOrderList(); + if (isAncillaryOrdersNeedReloading) { + patientAncillaryOrdersList.clear(); + isAncillaryOrdersLoading = true; + isAncillaryDetailsProceduresLoading = true; + notificationsCount = "0"; + getPatientOnlineAncillaryOrderList(); + } getPatientDashboard(); } bool isAncillaryOrdersLoading = false; bool isAncillaryDetailsProceduresLoading = false; bool isProcessingPayment = false; + bool isAncillaryOrdersNeedReloading = true; List patientAncillaryOrdersList = []; List patientAncillaryOrderProceduresList = []; @@ -32,6 +35,11 @@ class TodoSectionViewModel extends ChangeNotifier { notifyListeners(); } + void setIsAncillaryOrdersNeedReloading(bool value) { + isAncillaryOrdersNeedReloading = value; + notifyListeners(); + } + Future getPatientDashboard({Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await todoSectionRepo.getPatientDashboard(); @@ -55,6 +63,10 @@ class TodoSectionViewModel extends ChangeNotifier { } Future getPatientOnlineAncillaryOrderList({Function(dynamic)? onSuccess, Function(String)? onError}) async { + if (!isAncillaryOrdersNeedReloading) { + return; + } + patientAncillaryOrdersList.clear(); isAncillaryOrdersLoading = true; notifyListeners(); @@ -71,6 +83,7 @@ class TodoSectionViewModel extends ChangeNotifier { } else if (apiResponse.messageStatus == 1) { patientAncillaryOrdersList = apiResponse.data!; isAncillaryOrdersLoading = false; + isAncillaryOrdersNeedReloading = false; notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); diff --git a/lib/presentation/book_appointment/doctor_profile_page.dart b/lib/presentation/book_appointment/doctor_profile_page.dart index 72f242d..c62404a 100644 --- a/lib/presentation/book_appointment/doctor_profile_page.dart +++ b/lib/presentation/book_appointment/doctor_profile_page.dart @@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/doctor_rating_details.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; @@ -108,9 +109,21 @@ class DoctorProfilePage extends StatelessWidget { ), SizedBox(height: 16.h), "Ratings".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate.toString().toText16(isBold: true, color: AppColors.textColor), + bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate + .toString() + .toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor), ], - ), + ).onPress(() { + bookAppointmentsViewModel.getDoctorRatingDetails(); + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.doctorRating.tr(context: context), + context, + child: DoctorRatingDetails(), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }), SizedBox(width: 36.w), Column( children: [ @@ -122,15 +135,28 @@ class DoctorProfilePage extends StatelessWidget { ), SizedBox(height: 16.h), "Reviews".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate.toString().toText16(isBold: true, color: AppColors.textColor), + bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate + .toString() + .toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor), ], - ), + ).onPress(() { + bookAppointmentsViewModel.getDoctorRatingDetails(); + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.doctorRating.tr(context: context), + context, + child: DoctorRatingDetails(), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }), ], ), SizedBox(height: 16.h), Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), SizedBox(height: 16.h), - "Biography".toText14(weight: FontWeight.w600, color: AppColors.textColor), + LocaleKeys.docInfo.tr(context: context).toText14(weight: FontWeight.w600, color: AppColors.textColor), + SizedBox(height: 6.h), bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo!.toText12(fontWeight: FontWeight.w600, color: AppColors.greyTextColor), ], ).paddingSymmetrical(24.h, 0.h), diff --git a/lib/presentation/book_appointment/widgets/doctor_card.dart b/lib/presentation/book_appointment/widgets/doctor_card.dart index 97a941d..e2009bb 100644 --- a/lib/presentation/book_appointment/widgets/doctor_card.dart +++ b/lib/presentation/book_appointment/widgets/doctor_card.dart @@ -90,7 +90,7 @@ class DoctorCard extends StatelessWidget { Row( children: [ SizedBox( - width: MediaQuery.of(context).size.width * 0.49, + width: MediaQuery.of(context).size.width * 0.55, child: (isLoading ? "Dr John Smith" : "${doctorsListResponseModel.doctorTitle} ${doctorsListResponseModel.name}") .toString() .toText16(isBold: true, maxlines: 1), @@ -99,14 +99,14 @@ class DoctorCard extends StatelessWidget { ), SizedBox(height: 2.h), Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox( - width: MediaQuery.of(context).size.width * 0.45, child: (isLoading ? "Consultant Cardiologist" : doctorsListResponseModel.speciality!.isNotEmpty - ? doctorsListResponseModel.speciality!.first + ? (doctorsListResponseModel.speciality!.first.length > 32 + ? '${doctorsListResponseModel.speciality!.first.substring(0, 32)}...' + : doctorsListResponseModel.speciality!.first) : "") .toString() .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 2) @@ -121,6 +121,29 @@ class DoctorCard extends StatelessWidget { ).toShimmer2(isShow: isLoading), ], ), + SizedBox(height: 6.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget( + labelText: "${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}", + ).toShimmer2(isShow: isLoading), + AppCustomChipWidget( + labelText: "${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}", + ).toShimmer2(isShow: isLoading), + bookAppointmentsViewModel.isNearestAppointmentSelected + ? doctorsListResponseModel.nearestFreeSlot != null + ? AppCustomChipWidget( + labelText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)), + backgroundColor: AppColors.successColor, + textColor: AppColors.whiteColor, + ).toShimmer2(isShow: isLoading) + : SizedBox.shrink() + : SizedBox.shrink(), + ], + ), ], ), ), @@ -131,28 +154,6 @@ class DoctorCard extends StatelessWidget { ), ], ), - Wrap( - direction: Axis.horizontal, - spacing: 3.h, - runSpacing: 4.h, - children: [ - AppCustomChipWidget( - labelText: "${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}", - ).toShimmer2(isShow: isLoading), - AppCustomChipWidget( - labelText: "${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}", - ).toShimmer2(isShow: isLoading), - bookAppointmentsViewModel.isNearestAppointmentSelected - ? doctorsListResponseModel.nearestFreeSlot != null - ? AppCustomChipWidget( - labelText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)), - backgroundColor: AppColors.successColor, - textColor: AppColors.whiteColor, - ).toShimmer2(isShow: isLoading) - : SizedBox.shrink() - : SizedBox.shrink(), - ], - ), SizedBox(height: 12.h), CustomButton( text: LocaleKeys.bookAppo.tr(context: context), diff --git a/lib/presentation/book_appointment/widgets/doctor_rating_details.dart b/lib/presentation/book_appointment/widgets/doctor_rating_details.dart new file mode 100644 index 0000000..0ed5486 --- /dev/null +++ b/lib/presentation/book_appointment/widgets/doctor_rating_details.dart @@ -0,0 +1,195 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_rating_bar/flutter_rating_bar.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/string_extensions.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:provider/provider.dart'; + +class DoctorRatingDetails extends StatelessWidget { + const DoctorRatingDetails({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer(builder: (context, bookAppointmentsVM, child) { + return bookAppointmentsVM.isDoctorRatingDetailsLoading + ? Utils.getLoadingWidget() + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + bookAppointmentsVM.doctorsProfileResponseModel.actualDoctorRate!.ceilToDouble().toString().toText44(isBold: true), + SizedBox(height: 4.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "${bookAppointmentsVM.doctorsProfileResponseModel.noOfPatientsRate} ${LocaleKeys.reviews.tr(context: context)}" + .toText16(weight: FontWeight.w500, color: AppColors.greyInfoTextColor), + RatingBar( + initialRating: bookAppointmentsVM.doctorsProfileResponseModel.actualDoctorRate!.toDouble(), + direction: Axis.horizontal, + allowHalfRating: true, + itemCount: 5, + itemSize: 20.h, + ignoreGestures: true, + ratingWidget: RatingWidget( + full: Icon( + Icons.star, + color: AppColors.ratingColorYellow, + size: 24.h, + ), + half: Icon( + Icons.star_half, + color: AppColors.ratingColorYellow, + ), + empty: Icon( + Icons.star, + color: AppColors.ratingColorYellow, + ), + ), + tapOnlyMode: true, + unratedColor: Colors.grey[500], + itemPadding: EdgeInsets.symmetric(horizontal: 4.0), + onRatingUpdate: (rating) { + print(rating); + }, + ), + ], + ), + SizedBox(height: 8.h), + Container( + margin: EdgeInsets.only(top: 10.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Container( + width: 100.0, + margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0), + child: Text(LocaleKeys.excellent.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))), + getRatingLine(bookAppointmentsVM.doctorDetailsList[0].ratio, Colors.green[700]!), + ], + ), + Container( + margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0), + child: Text("${getRatingWidth(bookAppointmentsVM.doctorDetailsList[0].ratio).round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)), + ), + ], + ), + ), + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Container( + width: 100.0, + margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0), + child: Text(LocaleKeys.vGood.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))), + getRatingLine(bookAppointmentsVM.doctorDetailsList[1].ratio, Color(0xffB7B723)), + ], + ), + Container( + margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0), + child: Text("${bookAppointmentsVM.doctorDetailsList[1].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)), + ), + ], + ), + ), + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Container( + width: 100.0, + margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0), + child: Text(LocaleKeys.good.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))), + getRatingLine(bookAppointmentsVM.doctorDetailsList[2].ratio, Color(0xffEBA727)), + ], + ), + Container( + margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0), + child: Text("${bookAppointmentsVM.doctorDetailsList[2].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)), + ), + ], + ), + ), + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Container( + width: 100.0, + margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0), + child: Text(LocaleKeys.average.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))), + getRatingLine(bookAppointmentsVM.doctorDetailsList[3].ratio, Color(0xffEB7227)), + ], + ), + Container( + margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0), + child: Text("${bookAppointmentsVM.doctorDetailsList[3].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)), + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(bottom: 30.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Container( + width: 100.0, + margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0), + child: Text(LocaleKeys.average.tr(context: context), style: TextStyle(fontSize: 13.0, color: Colors.black, fontWeight: FontWeight.w600))), + getRatingLine(bookAppointmentsVM.doctorDetailsList[4].ratio, Color(0xffE20C0C)), + ], + ), + Container( + margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0), + child: Text("${bookAppointmentsVM.doctorDetailsList[4].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: Colors.black, fontWeight: FontWeight.w600)), + ), + ], + ), + ), + ], + ); + }); + } + + double getRatingWidth(num patientNumber) { + var width = patientNumber; + return width.roundToDouble(); + } + + Widget getRatingLine(double patientNumber, Color color) { + return Container( + margin: EdgeInsets.only(top: 10.0), + child: Stack(children: [ + SizedBox( + width: 150.0, + height: 7.h, + child: Container( + color: Colors.grey[300], + ), + ), + SizedBox( + width: patientNumber * 1.55, + height: 7.h, + child: Container( + color: color, + ), + ), + ]), + ); + } +} diff --git a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart index 3850c1f..999a43e 100644 --- a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart +++ b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart @@ -159,6 +159,15 @@ class ErOnlineCheckinHome extends StatelessWidget { await vm.getPatientERPaymentInformation(onSuccess: (response) { LoaderBottomSheet.hideLoader(); vm.navigateToEROnlineCheckInPaymentPage(); + }, onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err.toString()), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); }); }, onHospitalSearch: (value) { diff --git a/lib/presentation/medical_file/patient_sickleaves_list_page.dart b/lib/presentation/medical_file/patient_sickleaves_list_page.dart index fcdb12e..93cf17f 100644 --- a/lib/presentation/medical_file/patient_sickleaves_list_page.dart +++ b/lib/presentation/medical_file/patient_sickleaves_list_page.dart @@ -17,6 +17,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.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/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:open_filex/open_filex.dart'; import 'package:provider/provider.dart'; @@ -197,49 +198,23 @@ class _PatientSickleavesListPageState extends State { ], ), SizedBox(height: 8.h), - Row( + Wrap( + direction: Axis.horizontal, + spacing: 6.h, + runSpacing: 6.h, children: [ - CustomButton( - text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(sickLeave.appointmentDate), false), - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 24.h, + AppCustomChipWidget( + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(sickLeave.appointmentDate), false), ), - SizedBox(width: 8.h), - CustomButton( - text: model.isSickLeavesSortByClinic ? sickLeave.projectName! : sickLeave.clinicName!, - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 24.h, + AppCustomChipWidget( + labelText: model.isSickLeavesSortByClinic ? sickLeave.projectName! : sickLeave.clinicName!, ), - SizedBox(width: 8.h), - CustomButton( - text: "${sickLeave.sickLeaveDays} Days", - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 24.h, + AppCustomChipWidget( + labelText: "${sickLeave.sickLeaveDays} Days", ), ], ), - SizedBox(height: 8.h), + SizedBox(height: 12.h), Row( children: [ Expanded( diff --git a/lib/presentation/monthly_report/monthly_report.dart b/lib/presentation/monthly_report/monthly_report.dart index 474863b..43c7fc1 100644 --- a/lib/presentation/monthly_report/monthly_report.dart +++ b/lib/presentation/monthly_report/monthly_report.dart @@ -80,7 +80,10 @@ class MonthlyReport extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), - "${appState.getAuthenticatedUser()!.emailAddress}".toText16(color: AppColors.textColor, weight: FontWeight.w500), + SizedBox( + width: MediaQuery.of(context).size.width * 0.7, + child: "${appState.getAuthenticatedUser()!.emailAddress}".toText16(color: AppColors.textColor, weight: FontWeight.w500, maxlines: 2), + ), ], ), ], diff --git a/lib/presentation/prescriptions/prescription_detail_page.dart b/lib/presentation/prescriptions/prescription_detail_page.dart index 9b1ae6d..796bfdd 100644 --- a/lib/presentation/prescriptions/prescription_detail_page.dart +++ b/lib/presentation/prescriptions/prescription_detail_page.dart @@ -148,7 +148,7 @@ class _PrescriptionDetailPageState extends State { text: LocaleKeys.downloadPrescription.tr(context: context), onPressed: () async { LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPrescriptionPDFPleaseWait.tr(context: context)); - await prescriptionVM.getPrescriptionPDFBase64(widget.prescriptionsResponseModel).then((val) async { + await prescriptionVM.getPrescriptionPDFBase64(widget.prescriptionsResponseModel, onSuccess: (value) async { LoaderBottomSheet.hideLoader(); if (prescriptionVM.prescriptionPDFBase64Data.isNotEmpty) { String path = await Utils.createFileFromString(prescriptionVM.prescriptionPDFBase64Data, "pdf"); @@ -164,6 +164,15 @@ class _PrescriptionDetailPageState extends State { ); } } + }, onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); }); }, backgroundColor: AppColors.successColor.withValues(alpha: 0.15), diff --git a/lib/presentation/prescriptions/prescription_item_view.dart b/lib/presentation/prescriptions/prescription_item_view.dart index a2a3e7f..a41b53f 100644 --- a/lib/presentation/prescriptions/prescription_item_view.dart +++ b/lib/presentation/prescriptions/prescription_item_view.dart @@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_mo import 'package:hmg_patient_app_new/generated/locale_keys.g.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/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; @@ -56,74 +57,18 @@ class PrescriptionItemView extends StatelessWidget { spacing: 6.h, runSpacing: 6.h, children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - CustomButton( - text: "${LocaleKeys.route.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].route}", - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ).toShimmer2(isShow: isLoading), - ], - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - CustomButton( - text: "${LocaleKeys.frequency.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].frequency}", - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ).toShimmer2(isShow: isLoading), - ], - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - CustomButton( - text: "${LocaleKeys.dailyDoses.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].doseDailyQuantity}", - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ).toShimmer2(isShow: isLoading), - ], - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - CustomButton( - text: "${LocaleKeys.days.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].days}", - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ).toShimmer2(isShow: isLoading), - ], - ), + AppCustomChipWidget( + labelText: "${LocaleKeys.route.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].route}", + ).toShimmer2(isShow: isLoading), + AppCustomChipWidget( + labelText: "${LocaleKeys.frequency.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].frequency}", + ).toShimmer2(isShow: isLoading), + AppCustomChipWidget( + labelText: "${LocaleKeys.dailyDoses.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].doseDailyQuantity}", + ).toShimmer2(isShow: isLoading), + AppCustomChipWidget( + labelText: "${LocaleKeys.days.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].days}", + ).toShimmer2(isShow: isLoading), ], ).paddingSymmetrical(16.h, 0.h), SizedBox(height: 8.h), diff --git a/lib/presentation/prescriptions/prescriptions_list_page.dart b/lib/presentation/prescriptions/prescriptions_list_page.dart index e2a862b..c2d97f8 100644 --- a/lib/presentation/prescriptions/prescriptions_list_page.dart +++ b/lib/presentation/prescriptions/prescriptions_list_page.dart @@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.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/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; @@ -207,32 +208,16 @@ class _PrescriptionsListPageState extends State { ], ), SizedBox(height: 8.h), - Row( + Wrap( + direction: Axis.horizontal, + spacing: 6.h, + runSpacing: 6.h, children: [ - CustomButton( - text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescription.appointmentDate), false), - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 24.h, + AppCustomChipWidget( + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescription.appointmentDate), false), ), - SizedBox(width: 8.h), - CustomButton( - text: model.isSortByClinic ? prescription.name! : prescription.clinicDescription!, - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 24.h, + AppCustomChipWidget( + labelText: model.isSortByClinic ? prescription.name! : prescription.clinicDescription!, ), ], ), diff --git a/lib/presentation/todo_section/ancillary_procedures_details_page.dart b/lib/presentation/todo_section/ancillary_procedures_details_page.dart index 9e97509..eba80cd 100644 --- a/lib/presentation/todo_section/ancillary_procedures_details_page.dart +++ b/lib/presentation/todo_section/ancillary_procedures_details_page.dart @@ -658,6 +658,7 @@ class _AncillaryOrderDetailsListState extends State { text: LocaleKeys.proceedToPayment.tr(context: context), onPressed: () { // Navigate to payment page with selected procedures + todoSectionViewModel.setIsAncillaryOrdersNeedReloading(true); Navigator.of(context).push( CustomPageRoute( page: AncillaryOrderPaymentPage( From b309342cdca2991134465bb24bb487fe91e97390 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Mon, 2 Feb 2026 15:30:09 +0300 Subject: [PATCH 4/5] lab fixes --- lib/features/lab/lab_view_model.dart | 3 +++ .../hmg_services/services_page.dart | 18 +++++++++--------- lib/presentation/lab/alphabeticScroll.dart | 1 - 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart index 118c54a0..1047dbc 100644 --- a/lib/features/lab/lab_view_model.dart +++ b/lib/features/lab/lab_view_model.dart @@ -240,6 +240,9 @@ class LabViewModel extends ChangeNotifier { model: item)) }; uniqueTestsList = uniqueTests.toList(); + + uniqueTestsList = Utils.uniqueBy(uniqueTestsList, (p) => p.testCode); + uniqueTestsList.sort((a, b) { return a.description!.toLowerCase().compareTo(b.description!.toLowerCase()); }); diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart index 10b9342..ce2c537 100644 --- a/lib/presentation/hmg_services/services_page.dart +++ b/lib/presentation/hmg_services/services_page.dart @@ -157,15 +157,15 @@ class ServicesPage extends StatelessWidget { true, route: AppRoutes.bloodDonationPage, ), - HmgServicesComponentModel( - 3, - "My Child Vaccine".needTranslation, - "".needTranslation, - AppAssets.my_child_vaccine_icon, - bgColor: AppColors.myChildVaccineCardColor, - true, - route: AppRoutes.myChildVaccine, - ), + // HmgServicesComponentModel( + // 3, + // "My Child Vaccine".needTranslation, + // "".needTranslation, + // AppAssets.my_child_vaccine_icon, + // bgColor: AppColors.myChildVaccineCardColor, + // true, + // route: AppRoutes.myChildVaccine, + // ), // HmgServicesComponentModel( // 11, // "Covid 19 Test".needTranslation, diff --git a/lib/presentation/lab/alphabeticScroll.dart b/lib/presentation/lab/alphabeticScroll.dart index 29ded69..95d9da4 100644 --- a/lib/presentation/lab/alphabeticScroll.dart +++ b/lib/presentation/lab/alphabeticScroll.dart @@ -203,7 +203,6 @@ class _AlphabetScrollPageState extends State { verticalOffset: 100.0, child: FadeInAnimation( child: LabOrderByTest( - appState: getIt(), onTap: () { if (items[itemIndex].model != null) { From 85ccf41d04daa68b28a994f203ff0f7322820135 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 3 Feb 2026 14:58:37 +0300 Subject: [PATCH 5/5] Updates & fixes for Habib Wallet, Insurance update, Immediate LiveCare --- lib/core/api/api_client.dart | 2 +- lib/core/api_consts.dart | 2 +- lib/core/utils/utils.dart | 1 + .../habib_wallet/habib_wallet_view_model.dart | 6 + lib/features/insurance/insurance_repo.dart | 57 ++ .../insurance/insurance_view_model.dart | 41 ++ .../upload_insurance_card_response_model.dart | 1 + .../blood_donation/blood_donation_page.dart | 11 +- .../immediate_livecare_payment_details.dart | 496 +++++++++--------- .../call_ambulance/call_ambulance_page.dart | 6 +- .../habib_wallet/recharge_wallet_page.dart | 33 +- .../widgets/select-medical_file.dart | 25 +- .../insurance_update_details_card.dart | 44 +- .../my_family/widget/family_cards.dart | 19 +- lib/services/dialog_service.dart | 7 +- 15 files changed, 475 insertions(+), 276 deletions(-) create mode 100644 lib/features/insurance/models/resp_models/upload_insurance_card_response_model.dart diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 3eb8fa2..4286618 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -182,7 +182,7 @@ class ApiClientImp implements ApiClient { } // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 4769038; + // body['PatientID'] = 4768663; // body['PatientTypeID'] = 1; // body['PatientOutSA'] = 0; // body['SessionID'] = "45786230487560q"; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index bec908b..8319c9d 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -680,7 +680,7 @@ const DASHBOARD = 'Services/Patients.svc/REST/PatientDashboard'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 10d3767..4d8fec0 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -977,4 +977,5 @@ class Utils { return checkDate == today; } + } diff --git a/lib/features/habib_wallet/habib_wallet_view_model.dart b/lib/features/habib_wallet/habib_wallet_view_model.dart index 5291646..2f33895 100644 --- a/lib/features/habib_wallet/habib_wallet_view_model.dart +++ b/lib/features/habib_wallet/habib_wallet_view_model.dart @@ -9,6 +9,7 @@ class HabibWalletViewModel extends ChangeNotifier { bool isWalletAmountLoading = false; num habibWalletAmount = 0; num walletRechargeAmount = 0; + String notesText = ""; bool isBottomSheetContentLoading = false; @@ -60,6 +61,11 @@ class HabibWalletViewModel extends ChangeNotifier { notifyListeners(); } + setNotesText(String notes) { + notesText = notes; + notifyListeners(); + } + setDepositorDetails(String fileNum, String depositor, String mobile) { fileNumber = fileNum; depositorName = depositor; diff --git a/lib/features/insurance/insurance_repo.dart b/lib/features/insurance/insurance_repo.dart index 1aeb769..2719035 100644 --- a/lib/features/insurance/insurance_repo.dart +++ b/lib/features/insurance/insurance_repo.dart @@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patien import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_update_response_model.dart'; +import 'package:hmg_patient_app_new/features/insurance/models/resp_models/upload_insurance_card_response_model.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; abstract class InsuranceRepo { @@ -17,6 +18,14 @@ abstract class InsuranceRepo { Future>> getPatientInsuranceDetailsForUpdate({required String patientId, required String identificationNo}); Future>>> getPatientInsuranceApprovalsList(); + + Future>> updatePatientInsuranceCard({ + required int patientID, + required int patientType, + required String mobileNo, + required String patientIdentificationID, + required String insuranceCardImage, + }); } class InsuranceRepoImp implements InsuranceRepo { @@ -183,4 +192,52 @@ class InsuranceRepoImp implements InsuranceRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>> updatePatientInsuranceCard({ + required int patientID, + required int patientType, + required String mobileNo, + required String patientIdentificationID, + required String insuranceCardImage, + }) async { + Map mapDevice = { + "PatientID": patientID, + "PatientType": patientType, + "MobileNo": mobileNo, + "PatientIdentificationID": patientIdentificationID, + "InsuranceCardImage": insuranceCardImage, + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + UPLOAD_INSURANCE_CARD, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + // final uploadResponse = UploadInsuranceCardResponseModel.fromJson(response); + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: apiResponse, + ); + } 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())); + } + } } diff --git a/lib/features/insurance/insurance_view_model.dart b/lib/features/insurance/insurance_view_model.dart index 0bbdda2..261bda2 100644 --- a/lib/features/insurance/insurance_view_model.dart +++ b/lib/features/insurance/insurance_view_model.dart @@ -5,6 +5,7 @@ import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patien import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_update_response_model.dart'; +import 'package:hmg_patient_app_new/features/insurance/models/resp_models/upload_insurance_card_response_model.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; class InsuranceViewModel extends ChangeNotifier { @@ -171,4 +172,44 @@ class InsuranceViewModel extends ChangeNotifier { }, ); } + + Future updatePatientInsuranceCard({ + required int patientID, + required int patientType, + required String mobileNo, + required String patientIdentificationID, + required String insuranceCardImage, + Function(dynamic)? onSuccess, + Function(String)? onError, + }) async { + final result = await insuranceRepo.updatePatientInsuranceCard( + patientID: patientID, + patientType: patientType, + mobileNo: mobileNo, + patientIdentificationID: patientIdentificationID, + insuranceCardImage: insuranceCardImage, + ); + + result.fold( + (failure) async { + notifyListeners(); + if (onError != null) { + onError(failure.toString()); + } + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + notifyListeners(); + if (onError != null) { + onError(apiResponse.errorMessage ?? "Error updating insurance card"); + } + } else if (apiResponse.messageStatus == 1) { + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } } diff --git a/lib/features/insurance/models/resp_models/upload_insurance_card_response_model.dart b/lib/features/insurance/models/resp_models/upload_insurance_card_response_model.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/features/insurance/models/resp_models/upload_insurance_card_response_model.dart @@ -0,0 +1 @@ + diff --git a/lib/presentation/blood_donation/blood_donation_page.dart b/lib/presentation/blood_donation/blood_donation_page.dart index 26da599..7c6c625 100644 --- a/lib/presentation/blood_donation/blood_donation_page.dart +++ b/lib/presentation/blood_donation/blood_donation_page.dart @@ -263,13 +263,20 @@ class _BloodDonationPageState extends State { Row( children: [ Text( - LocaleKeys.iAcceptThe.tr(), + "${LocaleKeys.iAcceptThe.tr()} ", style: context.dynamicTextStyle(fontSize: 14.f, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), ), GestureDetector( onTap: () { // Navigate to terms and conditions page - Navigator.of(context).pushNamed('/terms'); + // Navigator.of(context).pushNamed('/terms'); + appState.isArabic() + ? Utils.openWebView( + url: 'https://hmg.com/ar/Pages/Terms.aspx', + ) + : Utils.openWebView( + url: 'https://hmg.com/en/Pages/Terms.aspx', + ); }, child: Text( LocaleKeys.termsConditoins.tr(), diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart index d522b45..6b2620b 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart @@ -33,71 +33,30 @@ import 'package:smooth_corner/smooth_corner.dart'; class ImmediateLiveCarePaymentDetails extends StatelessWidget { ImmediateLiveCarePaymentDetails({super.key}); - late ImmediateLiveCareViewModel immediateLiveCareViewModel; + // late ImmediateLiveCareViewModel immediateLiveCareViewModel; late AppState appState; @override Widget build(BuildContext context) { - immediateLiveCareViewModel = Provider.of(context, listen: false); + // immediateLiveCareViewModel = Provider.of(context, listen: false); appState = getIt.get(); return Scaffold( backgroundColor: AppColors.scaffoldBgColor, - body: Column( - children: [ - Expanded( - child: CollapsingListView( - title: LocaleKeys.reviewLiveCareRequest.tr(context: context), - child: SingleChildScrollView( - padding: EdgeInsets.symmetric(horizontal: 24.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 24.h), - LocaleKeys.patientInfo.tr(context: context).toText16(isBold: true), - SizedBox(height: 16.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.h, - hasShadow: false, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Row( - children: [ - Image.asset( - appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, - width: 52.h, - height: 52.h, - ), - SizedBox(width: 8.h), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true), - SizedBox(height: 8.h), - Wrap( - direction: Axis.horizontal, - spacing: 3.h, - runSpacing: 4.h, - children: [ - AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} ${LocaleKeys.yearsOld.tr(context: context)}"), - AppCustomChipWidget( - labelText: - "${LocaleKeys.clinic.tr()}: ${(appState.isArabic() ? immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceNameN : immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceName)!}"), - ], - ), - ], - ), - ], - ), - ), - ), - SizedBox(height: 24.h), - LocaleKeys.selectedLiveCareType.tr(context: context).toText16(isBold: true), - SizedBox(height: 16.h), - Consumer(builder: (context, bookAppointmentsVM, child) { - return Container( + body: Consumer(builder: (context, immediateLiveCareVM, child) { + return Column( + children: [ + Expanded( + child: CollapsingListView( + title: LocaleKeys.reviewLiveCareRequest.tr(context: context), + child: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 24.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 24.h), + LocaleKeys.patientInfo.tr(context: context).toText16(isBold: true), + SizedBox(height: 16.h), + Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 24.h, @@ -106,217 +65,262 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { child: Padding( padding: EdgeInsets.all(16.h), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( + Image.asset( + appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, + width: 52.h, + height: 52.h, + ), + SizedBox(width: 8.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain), - SizedBox(width: 8.h), - getLiveCareType(context, immediateLiveCareViewModel.liveCareSelectedCallType).toText16(isBold: true), + "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} ${LocaleKeys.yearsOld.tr(context: context)}"), + AppCustomChipWidget( + labelText: + "${LocaleKeys.clinic.tr()}: ${(appState.isArabic() ? immediateLiveCareVM.immediateLiveCareSelectedClinic.serviceNameN : immediateLiveCareVM.immediateLiveCareSelectedClinic.serviceName)!}"), + ], + ), ], ), - Utils.buildSvgWithAssets(icon: AppAssets.edit_icon, width: 24.h, height: 24.h, fit: BoxFit.contain), ], ), ), - ).onPress(() { - showCommonBottomSheetWithoutHeight(context, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareViewModel), callBackFunc: () async { - debugPrint("Selected Call Type: ${immediateLiveCareViewModel.liveCareSelectedCallType}"); - }, title: LocaleKeys.selectLiveCareCallType.tr(context: context), isCloseButtonVisible: true, isFullScreen: false); - }); - }), - SizedBox(height: 24.h) - ], + ), + SizedBox(height: 24.h), + LocaleKeys.selectedLiveCareType.tr(context: context).toText16(isBold: true), + SizedBox(height: 16.h), + Consumer(builder: (context, bookAppointmentsVM, child) { + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: false, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain), + SizedBox(width: 8.h), + getLiveCareType(context, immediateLiveCareVM.liveCareSelectedCallType).toText16(isBold: true), + ], + ), + Utils.buildSvgWithAssets(icon: AppAssets.edit_icon, width: 24.h, height: 24.h, fit: BoxFit.contain), + ], + ), + ), + ).onPress(() { + showCommonBottomSheetWithoutHeight(context, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareVM), callBackFunc: () async { + debugPrint("Selected Call Type: ${immediateLiveCareVM.liveCareSelectedCallType}"); + }, title: LocaleKeys.selectLiveCareCallType.tr(context: context), isCloseButtonVisible: true, isFullScreen: false); + }); + }), + SizedBox(height: 24.h) + ], + ), ), ), ), - ), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.h, - hasShadow: false, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.isCash ?? true) - ? Container( - height: 50.h, - decoration: ShapeDecoration( - color: AppColors.secondaryLightRedBorderColor, - shape: SmoothRectangleBorder( - borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), - smoothness: 1, + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: false, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.isCash ?? true) + ? Container( + height: 50.h, + decoration: ShapeDecoration( + color: AppColors.secondaryLightRedBorderColor, + shape: SmoothRectangleBorder( + borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), + smoothness: 1, + ), ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), - CustomButton( - text: LocaleKeys.updateInsurance.tr(context: context), - onPressed: () { - Navigator.of(context).push( - CustomPageRoute( - page: InsuranceHomePage(), - ), - ); - }, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.secondaryLightRedBorderColor, - textColor: AppColors.whiteColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(15, 0, 15, 0), - height: 30.h, - ).paddingSymmetrical(24.h, 0.h), - ], - ), - ) - : const SizedBox(), - SizedBox(height: 24.h), - LocaleKeys.totalAmountToPay.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 17.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - LocaleKeys.amountBeforeTax.tr(context: context).toText14(isBold: true), - Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.amount!.toText16(isBold: true), AppColors.blackColor, 13, - isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), - ], - ).paddingSymmetrical(24.h, 0.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - LocaleKeys.vat15.tr(context: context).toText14(isBold: true, color: AppColors.greyTextColor), - Utils.getPaymentAmountWithSymbol( - immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.tax!.toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13, - isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), - ], - ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 17.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SizedBox(width: 150.h, child: Utils.getPaymentMethods()), - Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!.toText24(isBold: true), AppColors.blackColor, 17, - isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), - ], - ).paddingSymmetrical(24.h, 0.h), - (immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total == "0" || immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total == "0.0") - ? CustomButton( - text: LocaleKeys.confirmLiveCare.tr(context: context), - onPressed: () async { - await askVideoCallPermission(context).then((val) async { - if (val) { - LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingLiveCareRequest.tr(context: context)); - - await immediateLiveCareViewModel.addNewCallRequestForImmediateLiveCare("${appState.getAuthenticatedUser()!.patientId}${DateTime.now().millisecondsSinceEpoch}"); - await immediateLiveCareViewModel.getPatientLiveCareHistory(); - LoaderBottomSheet.hideLoader(); - if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) { - Navigator.pushAndRemoveUntil( - context, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), + CustomButton( + text: LocaleKeys.updateInsurance.tr(context: context), + onPressed: () { + Navigator.of(context).push( CustomPageRoute( - page: LandingNavigation(), + page: InsuranceHomePage(), ), + ); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.secondaryLightRedBorderColor, + textColor: AppColors.whiteColor, + fontSize: 10, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(15, 0, 15, 0), + height: 30.h, + ).paddingSymmetrical(24.h, 0.h), + ], + ), + ) + : const SizedBox(), + SizedBox(height: 24.h), + LocaleKeys.totalAmountToPay.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 17.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.amountBeforeTax.tr(context: context).toText14(isBold: true), + Utils.getPaymentAmountWithSymbol(immediateLiveCareVM.liveCareImmediateAppointmentFeesList.amount!.toText16(isBold: true), AppColors.blackColor, 13, + isSaudiCurrency: immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), + ], + ).paddingSymmetrical(24.h, 0.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.vat15.tr(context: context).toText14(isBold: true, color: AppColors.greyTextColor), + Utils.getPaymentAmountWithSymbol( + immediateLiveCareVM.liveCareImmediateAppointmentFeesList.tax!.toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13, + isSaudiCurrency: immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), + ], + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 17.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox(width: 150.h, child: Utils.getPaymentMethods()), + Utils.getPaymentAmountWithSymbol(immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total!.toText24(isBold: true), AppColors.blackColor, 17, + isSaudiCurrency: immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), + ], + ).paddingSymmetrical(24.h, 0.h), + (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0" || immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0.0") + ? CustomButton( + text: LocaleKeys.confirmLiveCare.tr(context: context), + onPressed: () async { + await askVideoCallPermission(context).then((val) async { + if (val) { + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingLiveCareRequest.tr(context: context)); + + await immediateLiveCareVM.addNewCallRequestForImmediateLiveCare("${appState.getAuthenticatedUser()!.patientId}${DateTime + .now() + .millisecondsSinceEpoch}"); + await immediateLiveCareVM.getPatientLiveCareHistory(); + LoaderBottomSheet.hideLoader(); + if (immediateLiveCareVM.patientHasPendingLiveCareRequest) { + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), (r) => false); - Navigator.of(context).push( - CustomPageRoute( - page: ImmediateLiveCarePendingRequestPage(), - ), - ); + Navigator.of(context).push( + CustomPageRoute( + page: ImmediateLiveCarePendingRequestPage(), + ), + ); + } else { + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: LocaleKeys.unknownErrorOccurred.tr(context: context)), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } } else { showCommonBottomSheetWithoutHeight( + title: LocaleKeys.notice.tr(context: context), context, - child: Utils.getErrorWidget(loadingText: LocaleKeys.unknownErrorOccurred.tr(context: context)), + child: Utils.getWarningWidget( + loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context), + isShowActionButtons: true, + onCancelTap: () { + Navigator.pop(context); + }, + onConfirmTap: () async { + openAppSettings(); + }), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, ); } - } else { - showCommonBottomSheetWithoutHeight( - title: LocaleKeys.notice.tr(context: context), - context, - child: Utils.getWarningWidget( - loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context), - isShowActionButtons: true, - onCancelTap: () { - Navigator.pop(context); - }, - onConfirmTap: () async { - openAppSettings(); - }), - callBackFunc: () {}, - isFullScreen: false, - isCloseButtonVisible: true, - ); - } - }); - }, - backgroundColor: AppColors.successColor, - borderColor: AppColors.successColor, - textColor: AppColors.whiteColor, - fontSize: 16, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 50.h, - icon: AppAssets.livecare_book_icon, - iconColor: AppColors.whiteColor, - iconSize: 18.h, - ).paddingSymmetrical(24.h, 24.h) - : CustomButton( - text: LocaleKeys.payNow.tr(context: context), - onPressed: () async { - await askVideoCallPermission(context).then((val) { - if (val) { - Navigator.of(context).push( - CustomPageRoute( - page: ImmediateLiveCarePaymentPage(), - ), - ); - } - // else { - // showCommonBottomSheetWithoutHeight( - // title: LocaleKeys.notice.tr(context: context), - // context, - // child: Utils.getWarningWidget( - // loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context), - // isShowActionButtons: true, - // onCancelTap: () { - // Navigator.pop(context); - // }, - // onConfirmTap: () async { - // openAppSettings(); - // }), - // callBackFunc: () {}, - // isFullScreen: false, - // isCloseButtonVisible: true, - // ); - // } - }); - }, - backgroundColor: AppColors.infoColor, - borderColor: AppColors.infoColor, - textColor: AppColors.whiteColor, - fontSize: 16, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 50.h, - icon: AppAssets.appointment_pay_icon, - iconColor: AppColors.whiteColor, - iconSize: 18.h, - ).paddingSymmetrical(24.h, 24.h), - ], + }); + }, + backgroundColor: AppColors.successColor, + borderColor: AppColors.successColor, + textColor: AppColors.whiteColor, + fontSize: 16, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 50.h, + icon: AppAssets.livecare_book_icon, + iconColor: AppColors.whiteColor, + iconSize: 18.h, + ).paddingSymmetrical(24.h, 24.h) + : CustomButton( + text: LocaleKeys.payNow.tr(context: context), + onPressed: () async { + await askVideoCallPermission(context).then((val) { + if (val) { + Navigator.of(context).push( + CustomPageRoute( + page: ImmediateLiveCarePaymentPage(), + ), + ); + } + // else { + // showCommonBottomSheetWithoutHeight( + // title: LocaleKeys.notice.tr(context: context), + // context, + // child: Utils.getWarningWidget( + // loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context), + // isShowActionButtons: true, + // onCancelTap: () { + // Navigator.pop(context); + // }, + // onConfirmTap: () async { + // openAppSettings(); + // }), + // callBackFunc: () {}, + // isFullScreen: false, + // isCloseButtonVisible: true, + // ); + // } + }); + }, + backgroundColor: AppColors.infoColor, + borderColor: AppColors.infoColor, + textColor: AppColors.whiteColor, + fontSize: 16, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 50.h, + icon: AppAssets.appointment_pay_icon, + iconColor: AppColors.whiteColor, + iconSize: 18.h, + ).paddingSymmetrical(24.h, 24.h), + ], + ), ), - ), - ], - ), + ], + ); + }), ); } diff --git a/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart b/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart index e86104b..5a368a3 100644 --- a/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart +++ b/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart @@ -526,8 +526,8 @@ class CallAmbulancePage extends StatelessWidget { textPlaceInput(context) { return Consumer(builder: (_, vm, __) { - print("the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}"); - return SizedBox( + // print("the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}"); + return (vm.geocodeResponse != null && vm.geocodeResponse!.results.isNotEmpty) ? SizedBox( width: MediaQuery.sizeOf(context).width, child: TextInputWidget( labelText: LocaleKeys.enterPickupLocationManually.tr(context: context), @@ -549,7 +549,7 @@ class CallAmbulancePage extends StatelessWidget { ).onPress(() { openLocationInputBottomSheet(context); }), - ); + ) : SizedBox.shrink(); }); } diff --git a/lib/presentation/habib_wallet/recharge_wallet_page.dart b/lib/presentation/habib_wallet/recharge_wallet_page.dart index b8e2d01..22e854a 100644 --- a/lib/presentation/habib_wallet/recharge_wallet_page.dart +++ b/lib/presentation/habib_wallet/recharge_wallet_page.dart @@ -36,6 +36,7 @@ class _RechargeWalletPageState extends State { late HabibWalletViewModel habibWalletVM; late AppState appState; final TextEditingController amountTextController = TextEditingController(); + final TextEditingController notesTextController = TextEditingController(); @override void initState() { @@ -209,23 +210,20 @@ class _RechargeWalletPageState extends State { SizedBox(height: 16.h), Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), SizedBox(height: 16.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.notes_icon, width: 40.h, height: 40.h), - SizedBox(width: 8.h), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.notes.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), - "Lorem Ipsum".toText14(color: AppColors.textColor, weight: FontWeight.w500, letterSpacing: -0.2), - ], - ), - ], - ), - ], + TextInputWidget( + labelText: LocaleKeys.notes.tr(context: context), + hintText: "", + controller: notesTextController, + keyboardType: TextInputType.text, + isEnable: true, + prefix: null, + autoFocus: true, + isAllowRadius: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + leadingIcon: AppAssets.notes_icon, + errorMessage: LocaleKeys.enterValidIDorIqama.tr(context: context), + hasError: false, ), SizedBox(height: 8.h), ], @@ -271,6 +269,7 @@ class _RechargeWalletPageState extends State { ); } else { habibWalletVM.setWalletRechargeAmount(num.parse(amountTextController.text)); + habibWalletVM.setNotesText(notesTextController.text); // habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId.toString(), "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}", // appState.getAuthenticatedUser()!.mobileNumber!); Navigator.of(context).push( diff --git a/lib/presentation/habib_wallet/widgets/select-medical_file.dart b/lib/presentation/habib_wallet/widgets/select-medical_file.dart index aaa0036..6fed652 100644 --- a/lib/presentation/habib_wallet/widgets/select-medical_file.dart +++ b/lib/presentation/habib_wallet/widgets/select-medical_file.dart @@ -28,6 +28,8 @@ 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/features/habib_wallet/habib_wallet_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -145,6 +147,10 @@ class _MultiPageBottomSheetState extends State { ], ).paddingAll(16.h), ).onPress(() { + habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId!.toString(), + "${appState.getAuthenticatedUser()!.firstName.toString()} ${appState.getAuthenticatedUser()!.lastName.toString()}", appState.getAuthenticatedUser()!.mobileNumber!.toString()); + habibWalletVM.setCurrentIndex(0); + habibWalletVM.setSelectedRechargeType(1); Navigator.of(context).pop(); }), SizedBox(height: 16.h), @@ -167,7 +173,24 @@ class _MultiPageBottomSheetState extends State { Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h), ], ).paddingAll(16.h), - ), + ).onPress(() { + DialogService dialogService = getIt.get(); + dialogService.showFamilyBottomSheetWithoutH( + label: LocaleKeys.familyTitle.tr(context: context), + message: "", + isShowManageButton: false, + isForWalletRecharge: true, + onSwitchPress: (FamilyFileResponseModelLists profile) { + habibWalletVM.setDepositorDetails(profile.responseId.toString(), profile.patientName.toString(), profile.mobileNumber.toString()); + habibWalletVM.setCurrentIndex(0); + habibWalletVM.setSelectedRechargeType(2); + Navigator.of(context).pop(); + Navigator.of(context).pop(); + + // medicalFileViewModel.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber); + }, + profiles: getIt.get().patientFamilyFiles); + }), SizedBox(height: 16.h), Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( diff --git a/lib/presentation/insurance/widgets/insurance_update_details_card.dart b/lib/presentation/insurance/widgets/insurance_update_details_card.dart index c737732..35be7f9 100644 --- a/lib/presentation/insurance/widgets/insurance_update_details_card.dart +++ b/lib/presentation/insurance/widgets/insurance_update_details_card.dart @@ -1,6 +1,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.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/dependencies.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/string_extensions.dart'; @@ -11,16 +13,20 @@ import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:provider/provider.dart'; class PatientInsuranceCardUpdateCard extends StatelessWidget { PatientInsuranceCardUpdateCard({super.key}); late InsuranceViewModel insuranceViewModel; + late AppState appState; @override Widget build(BuildContext context) { insuranceViewModel = Provider.of(context); + appState = getIt.get(); return Column( mainAxisSize: MainAxisSize.min, children: [ @@ -49,7 +55,7 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "Haroon Amjad".toText16(weight: FontWeight.w600), + insuranceViewModel.patientInsuranceUpdateResponseModel!.memberName!.toText16(weight: FontWeight.w600), "Policy: ${insuranceViewModel.patientInsuranceUpdateResponseModel!.policyNumber}".toText12(isBold: true, color: AppColors.lightGrayColor), SizedBox(height: 8.h), Row( @@ -99,7 +105,41 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget { iconColor: AppColors.whiteColor, iconSize: 20.w, text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}", - onPressed: () {}, + onPressed: () { + LoaderBottomSheet.showLoader(); + insuranceViewModel.updatePatientInsuranceCard( + patientID: appState.getAuthenticatedUser()!.patientId!, + patientType: appState.getAuthenticatedUser()!.patientType!, + patientIdentificationID: appState.getAuthenticatedUser()!.patientIdentificationNo!, + mobileNo: appState.getAuthenticatedUser()!.mobileNumber!, + insuranceCardImage: "", + onSuccess: (val) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.success.tr(context: context), + context, + child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr(context: context)), + callBackFunc: () { + Navigator.pop(context); + }, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }, + onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.notice.tr(context: context), + context, + child: Utils.getErrorWidget(loadingText: err.toString()), + callBackFunc: () { + Navigator.pop(context); + }, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }); + }, backgroundColor: insuranceViewModel.patientInsuranceUpdateResponseModel != null ? AppColors.successColor : AppColors.lightGrayBGColor, borderColor: AppColors.successColor.withOpacity(0.01), textColor: AppColors.whiteColor, diff --git a/lib/presentation/my_family/widget/family_cards.dart b/lib/presentation/my_family/widget/family_cards.dart index 175cfb1..ea305b0 100644 --- a/lib/presentation/my_family/widget/family_cards.dart +++ b/lib/presentation/my_family/widget/family_cards.dart @@ -8,6 +8,7 @@ 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/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart'; @@ -25,6 +26,7 @@ class FamilyCards extends StatefulWidget { final bool isRequestDesign; final bool isLeftAligned; final bool isShowRemoveButton; + final bool isForWalletRecharge; const FamilyCards( {super.key, @@ -35,6 +37,7 @@ class FamilyCards extends StatefulWidget { this.isBottomSheet = false, this.isRequestDesign = false, this.isLeftAligned = false, + this.isForWalletRecharge = false, this.isShowRemoveButton = false}); @override @@ -206,7 +209,21 @@ class _FamilyCardsState extends State { height: 4.h, ), Spacer(), - CustomButton( + widget.isForWalletRecharge ? CustomButton( + height: 40.h, + onPressed: () { + widget.onSelect(profile); + // if (canSwitch) widget.onSelect(profile); + }, + text: LocaleKeys.select.tr(context: context), + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 13.h, + icon: AppAssets.activeCheck, + iconColor: isActive || !canSwitch ? (isActive ? null : AppColors.greyTextColor) : AppColors.primaryRedColor, + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0), + ).paddingOnly(top: 0, bottom: 0) : CustomButton( height: 40.h, onPressed: () { if (canSwitch) widget.onSelect(profile); diff --git a/lib/services/dialog_service.dart b/lib/services/dialog_service.dart index b185d89..0b3a521 100644 --- a/lib/services/dialog_service.dart +++ b/lib/services/dialog_service.dart @@ -32,7 +32,8 @@ abstract class DialogService { required String message, required Function(FamilyFileResponseModelLists response) onSwitchPress, required List profiles, - bool isShowManageButton = false}); + bool isShowManageButton = false, + bool isForWalletRecharge = false}); Future showFamilyBottomSheetWithoutHWithChild({String? label, required String message, Widget? child, required Function() onOkPressed, Function()? onCancelPressed}); @@ -143,7 +144,8 @@ class DialogServiceImp implements DialogService { required String message, required Function(FamilyFileResponseModelLists response) onSwitchPress, required List profiles, - bool isShowManageButton = false}) async { + bool isShowManageButton = false, + bool isForWalletRecharge = false}) async { final context = navigationService.navigatorKey.currentContext; if (context == null) return; showCommonBottomSheetWithoutHeight(context, @@ -161,6 +163,7 @@ class DialogServiceImp implements DialogService { }, onRemove: (FamilyFileResponseModelLists profile) {}, isShowDetails: false, + isForWalletRecharge: isForWalletRecharge, ), SizedBox(height: isShowManageButton ? 15.h : 24.h), if (isShowManageButton)