From db979977ca9ad6db0cfab4fcab2c6a51f4950b66 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Sun, 5 Oct 2025 12:22:33 +0300 Subject: [PATCH 1/5] Tamara implementation & enhancements --- lib/core/api/api_client.dart | 2 + lib/core/api_consts.dart | 16 +++ .../medical_file/medical_file_view_model.dart | 33 +++-- ...a_installments_details_response_model.dart | 83 +++++++++++ .../my_appointments/my_appointments_repo.dart | 40 ++++++ .../my_appointments_view_model.dart | 47 +++++- .../appointment_payment_page.dart | 31 ++-- .../immediate_livecare_payment_page.dart | 25 +++- lib/presentation/home/landing_page.dart | 4 +- .../lab/lab_result_item_view.dart | 135 ++++++++++-------- .../medical_file/medical_file_page.dart | 4 +- .../patient_sickleaves_list_page.dart | 6 +- .../medical_file/vaccine_list_page.dart | 20 +-- .../medical_file/widgets/lab_rad_card.dart | 2 +- .../widgets/patient_sick_leave_card.dart | 5 +- lib/widgets/in_app_browser/InAppBrowser.dart | 18 +-- 16 files changed, 352 insertions(+), 119 deletions(-) create mode 100644 lib/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index a064f11..9f0d230 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -34,6 +34,7 @@ abstract class ApiClient { required Function(dynamic response, int statusCode) onSuccess, required Function(String error, int statusCode) onFailure, Map? queryParams, + bool isAllowAny, bool isExternal, bool isRCService, }); @@ -324,6 +325,7 @@ class ApiClientImp implements ApiClient { {required Function(dynamic response, int statusCode) onSuccess, required Function(String error, int statusCode) onFailure, Map? queryParams, + bool isAllowAny = false, bool isExternal = false, bool isRCService = false}) async { String url; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 1c6cec1..8225796 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -743,6 +743,9 @@ class ApiConsts { static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL LIVE // static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; // Payfort Payment Gateway URL UAT + static String TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout"; + static String GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments"; + // var payFortEnvironment = FortEnvironment.test; // var applePayMerchantId = "merchant.com.hmgwebservices.uat"; @@ -753,18 +756,25 @@ class ApiConsts { payFortEnvironment = FortEnvironment.production; applePayMerchantId = "merchant.com.hmgwebservices"; SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; + TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout"; + GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments"; break; case AppEnvironmentTypeEnum.dev: baseUrl = "https://uat.hmgwebservices.com/"; payFortEnvironment = FortEnvironment.test; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; + TAMARA_URL = "https://mdlaboratories.com/tamara/Home/Checkout"; + GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamara/Home/GetInstallments"; break; case AppEnvironmentTypeEnum.uat: baseUrl = "https://uat.hmgwebservices.com/"; payFortEnvironment = FortEnvironment.test; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; + TAMARA_URL = "https://mdlaboratories.com/tamara/Home/Checkout"; + // GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamara/Home/GetInstallments"; + GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments"; break; case AppEnvironmentTypeEnum.preProd: @@ -772,18 +782,24 @@ class ApiConsts { payFortEnvironment = FortEnvironment.production; applePayMerchantId = "merchant.com.hmgwebservices"; SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; + TAMARA_URL = "https://mdlaboratories.com/tamara/Home/Checkout"; + GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamara/Home/GetInstallments"; break; case AppEnvironmentTypeEnum.qa: baseUrl = "https://uat.hmgwebservices.com/"; payFortEnvironment = FortEnvironment.test; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; + TAMARA_URL = "https://mdlaboratories.com/tamara/Home/Checkout"; + GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamara/Home/GetInstallments"; break; case AppEnvironmentTypeEnum.staging: baseUrl = "https://uat.hmgwebservices.com/"; payFortEnvironment = FortEnvironment.test; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; + TAMARA_URL = "https://mdlaboratories.com/tamara/Home/Checkout"; + GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamara/Home/GetInstallments"; break; } } diff --git a/lib/features/medical_file/medical_file_view_model.dart b/lib/features/medical_file/medical_file_view_model.dart index 95c5d63..3f0573f 100644 --- a/lib/features/medical_file/medical_file_view_model.dart +++ b/lib/features/medical_file/medical_file_view_model.dart @@ -108,12 +108,17 @@ class MedicalFileViewModel extends ChangeNotifier { final result = await medicalFileRepo.getPatientVaccinesList(); result.fold( - (failure) async => await errorHandlerService.handleError( - failure: failure, - onOkPressed: () { - onError!(failure.message); - }, - ), + // (failure) async => await errorHandlerService.handleError( + // failure: failure, + // onOkPressed: () { + // onError!(failure.message); + // }, + // ), + (failure) async { + // onError!(failure.message); + isPatientVaccineListLoading = false; + notifyListeners(); + }, (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); @@ -134,12 +139,16 @@ class MedicalFileViewModel extends ChangeNotifier { final result = await medicalFileRepo.getPatientSickLeavesList(); result.fold( - (failure) async => await errorHandlerService.handleError( - failure: failure, - onOkPressed: () { - onError!(failure.message); - }, - ), + // (failure) async => await errorHandlerService.handleError( + // failure: failure, + // onOkPressed: () { + // onError!(failure.message); + // }, + // ), + (failure) async { + isPatientSickLeaveListLoading = false; + notifyListeners(); + }, (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); diff --git a/lib/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart b/lib/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart new file mode 100644 index 0000000..59ad707 --- /dev/null +++ b/lib/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart @@ -0,0 +1,83 @@ +class GetTamaraInstallmentsDetailsResponseModel { + String? name; + String? description; + MinLimit? minLimit; + MinLimit? maxLimit; + List? supportedInstalments; + + GetTamaraInstallmentsDetailsResponseModel({this.name, this.description, this.minLimit, this.maxLimit, this.supportedInstalments}); + + GetTamaraInstallmentsDetailsResponseModel.fromJson(Map json) { + name = json['name']; + description = json['description']; + minLimit = json['minLimit'] != null ? new MinLimit.fromJson(json['minLimit']) : null; + maxLimit = json['maxLimit'] != null ? new MinLimit.fromJson(json['maxLimit']) : null; + if (json['supportedInstalments'] != null) { + supportedInstalments = []; + json['supportedInstalments'].forEach((v) { + supportedInstalments!.add(new SupportedInstalments.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['name'] = this.name; + data['description'] = this.description; + if (this.minLimit != null) { + data['minLimit'] = this.minLimit!.toJson(); + } + if (this.maxLimit != null) { + data['maxLimit'] = this.maxLimit!.toJson(); + } + if (this.supportedInstalments != null) { + data['supportedInstalments'] = this.supportedInstalments!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class MinLimit { + String? currency; + num? amount; + + MinLimit({this.currency, this.amount}); + + MinLimit.fromJson(Map json) { + currency = json['currency']; + amount = json['amount']; + } + + Map toJson() { + final Map data = new Map(); + data['currency'] = this.currency; + data['amount'] = this.amount; + return data; + } +} + +class SupportedInstalments { + int? instalments; + MinLimit? minLimit; + MinLimit? maxLimit; + + SupportedInstalments({this.instalments, this.minLimit, this.maxLimit}); + + SupportedInstalments.fromJson(Map json) { + instalments = json['instalments']; + minLimit = json['minLimit'] != null ? new MinLimit.fromJson(json['minLimit']) : null; + maxLimit = json['maxLimit'] != null ? new MinLimit.fromJson(json['maxLimit']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['instalments'] = this.instalments; + if (this.minLimit != null) { + data['minLimit'] = this.minLimit!.toJson(); + } + if (this.maxLimit != null) { + data['maxLimit'] = this.maxLimit!.toJson(); + } + return data; + } +} diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index eb8611d..b94f004 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/core/cache_consts.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel; 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/models/resp_models/patient_appointment_share_response_model.dart'; @@ -44,6 +45,8 @@ abstract class MyAppointmentsRepo { Future>>> getPatientDoctorsList(); Future>> insertLiveCareVIDARequest({required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}); + + Future>> getTamaraInstallmentsDetails(); } class MyAppointmentsRepoImp implements MyAppointmentsRepo { @@ -543,4 +546,41 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>> getTamaraInstallmentsDetails() async { + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.get( + ApiConsts.GET_TAMARA_INSTALLMENTS_URL, + isExternal: true, + isAllowAny: true, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response; + + final tamaraInstallmentsList = GetTamaraInstallmentsDetailsResponseModel.fromJson(list.first); + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: tamaraInstallmentsList, + ); + } 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/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index c553f0e..acf0a39 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -1,12 +1,11 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_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/models/resp_models/patient_appointment_share_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; -import '../../core/utils/doctor_response_mapper.dart' show DoctorMapper; - class MyAppointmentsViewModel extends ChangeNotifier { int selectedTabIndex = 0; @@ -32,6 +31,9 @@ class MyAppointmentsViewModel extends ChangeNotifier { PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel; + GetTamaraInstallmentsDetailsResponseModel? getTamaraInstallmentsDetailsResponseModel; + bool isTamaraDetailsLoading = false; + MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService, required this.appState}); void onTabChange(int index) { @@ -50,6 +52,7 @@ class MyAppointmentsViewModel extends ChangeNotifier { patientMyDoctorsList.clear(); isPatientMyDoctorsLoading = true; } + isTamaraDetailsLoading = true; isAppointmentPatientShareLoading = true; notifyListeners(); } @@ -79,6 +82,11 @@ class MyAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } + setIsTamaraDetailsLoading(bool val) { + isTamaraDetailsLoading = val; + notifyListeners(); + } + setAppointmentReminder(bool value, PatientAppointmentHistoryResponseModel item) { int index = patientAppointmentsHistoryList.indexOf(item); if (index != -1) { @@ -142,7 +150,13 @@ class MyAppointmentsViewModel extends ChangeNotifier { final result = await myAppointmentsRepo.getPatientShareAppointment(projectID: projectID, clinicID: clinicID, appointmentNo: appointmentNo, isLiveCareAppointment: isLiveCareAppointment); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async { + await errorHandlerService.handleError( + failure: failure, + onOkPressed: () { + onError!(failure.message); + }); + }, (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); @@ -337,4 +351,31 @@ class MyAppointmentsViewModel extends ChangeNotifier { }, ); } + + Future getTamaraInstallmentsDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.getTamaraInstallmentsDetails(); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + getTamaraInstallmentsDetailsResponseModel = apiResponse.data!; + isTamaraDetailsLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + + // if (apiResponse.messageStatus == 2) { + // onError!(apiResponse.errorMessage!); + // // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + // } else if (apiResponse.messageStatus == 1) { + // getTamaraInstallmentsDetailsResponseModel = apiResponse.data!; + // notifyListeners(); + // if (onSuccess != null) { + // onSuccess(apiResponse); + // } + // } + }, + ); + } } diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index 7334369..ee50ab4 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -51,17 +51,28 @@ class _AppointmentPaymentPageState extends State { String transID = ""; + bool isShowTamara = false; + @override void initState() { scheduleMicrotask(() { payfortViewModel.initPayfortViewModel(); + myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) { + if (myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! && + myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) { + setState(() { + isShowTamara = true; + }); + } + }); payfortViewModel.setIsApplePayConfigurationLoading(false); myAppointmentsViewModel.getPatientShareAppointment( widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.clinicID, - widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), - widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, - ); + widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, onError: (err) { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }); }); super.initState(); } @@ -162,11 +173,12 @@ class _AppointmentPaymentPageState extends State { openPaymentURL("visa"); }), SizedBox(height: 16.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: false, + isShowTamara + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, ), child: Row( mainAxisSize: MainAxisSize.max, @@ -196,7 +208,8 @@ class _AppointmentPaymentPageState extends State { ).paddingSymmetrical(24.h, 0.h).onPress(() { selectedPaymentMethod = "TAMARA"; openPaymentURL("tamara"); - }), + }) + : SizedBox.shrink(), ], ), ), diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart index d89ef33..5437945 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart @@ -56,10 +56,20 @@ class _ImmediateLiveCarePaymentPageState extends State= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! && + num.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!) <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) { + setState(() { + isShowTamara = true; + }); + } + }); }); super.initState(); } @@ -67,9 +77,9 @@ class _ImmediateLiveCarePaymentPageState extends State(); - myAppointmentsViewModel = Provider.of(context); + myAppointmentsViewModel = Provider.of(context, listen: false); immediateLiveCareViewModel = Provider.of(context, listen: false); - payfortViewModel = Provider.of(context); + payfortViewModel = Provider.of(context, listen: false); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: Consumer(builder: (context, myAppointmentsVM, child) { @@ -161,9 +171,10 @@ class _ImmediateLiveCarePaymentPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - "Quick Links".toText16(isBold: true), + "Quick Links".needTranslation.toText16(isBold: true), Row( children: [ - "View medical file".toText12(color: AppColors.primaryRedColor), + "View medical file".needTranslation.toText12(color: AppColors.primaryRedColor), SizedBox(width: 2.h), Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), ], diff --git a/lib/presentation/lab/lab_result_item_view.dart b/lib/presentation/lab/lab_result_item_view.dart index 03f721e..57a10ae 100644 --- a/lib/presentation/lab/lab_result_item_view.dart +++ b/lib/presentation/lab/lab_result_item_view.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.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/lab/models/resp_models/patient_lab_orders_response_model.dart'; @@ -49,7 +50,7 @@ class LabResultItemView extends StatelessWidget { backgroundColor: getLabOrderStatusColor(labOrder?.status ?? 0).withOpacity(0.15), textColor: getLabOrderStatusColor(labOrder?.status ?? 0), ).toShimmer2(isShow: isLoading, width: 100), - if (!isLoading) Icon(isExpanded ? Icons.expand_less : Icons.expand_more), + // if (!isLoading) Icon(isExpanded ? Icons.expand_less : Icons.expand_more), ], ), SizedBox(height: 8.h), @@ -59,7 +60,7 @@ class LabResultItemView extends StatelessWidget { isLoading ? "" : labOrder!.doctorImageURL!, width: 24.h, height: 24.h, - fit: BoxFit.fill, + fit: BoxFit.cover, errorBuilder: (cxt, child, tr) { return SizedBox(height: 24, width: 24); }, @@ -69,71 +70,83 @@ class LabResultItemView extends StatelessWidget { ], ), SizedBox(height: 8.h), - Wrap( - spacing: 8.h, - runSpacing: 0.h, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - AppCustomChipWidget(labelText: isLoading ? "null" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(labOrder!.createdOn), false)).toShimmer2(isShow: isLoading, width: 70), - AppCustomChipWidget(labelText: isLoading ? "null" : labOrder!.clinicDescription!).toShimmer2(isShow: isLoading, width: 100), + Wrap( + spacing: 8.h, + runSpacing: 0.h, + children: [ + AppCustomChipWidget(labelText: isLoading ? "null" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(labOrder!.createdOn), false)).toShimmer2(isShow: isLoading, width: 70), + AppCustomChipWidget(labelText: isLoading ? "null" : labOrder!.clinicDescription!).toShimmer2(isShow: isLoading, width: 100), + ], + ), + Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon_small, + iconColor: AppColors.textColor, + width: 20.h, + height: 14.h, + fit: BoxFit.contain, + ), ], ), ], ), ), - AnimatedSwitcher( - duration: Duration(milliseconds: 300), - switchInCurve: Curves.easeIn, - switchOutCurve: Curves.easeOut, - transitionBuilder: (Widget child, Animation animation) { - return FadeTransition( - opacity: animation, - child: SizeTransition( - sizeFactor: animation, - axisAlignment: 0.0, - child: child, - ), - ); - }, - child: isExpanded - ? Container( - key: ValueKey(index), - padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ...labOrder!.testDetails!.map((detail) { - return Padding( - padding: EdgeInsets.only(bottom: 8.h), - child: '● ${detail.description}'.toText14(weight: FontWeight.w500), - ); - }).toList(), - SizedBox(height: 16.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SizedBox(), - CustomButton( - icon: AppAssets.view_report_icon, - iconColor: AppColors.primaryRedColor, - iconSize: 16.h, - text: LocaleKeys.viewReport.tr(context: context), - onPressed: () {}, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 14, - fontWeight: FontWeight.bold, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - ), - ], - ), - ], - ), - ) - : SizedBox.shrink(key: ValueKey(-index)), - ), + // AnimatedSwitcher( + // duration: Duration(milliseconds: 300), + // switchInCurve: Curves.easeIn, + // switchOutCurve: Curves.easeOut, + // transitionBuilder: (Widget child, Animation animation) { + // return FadeTransition( + // opacity: animation, + // child: SizeTransition( + // sizeFactor: animation, + // axisAlignment: 0.0, + // child: child, + // ), + // ); + // }, + // child: isExpanded + // ? Container( + // key: ValueKey(index), + // padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // ...labOrder!.testDetails!.map((detail) { + // return Padding( + // padding: EdgeInsets.only(bottom: 8.h), + // child: '● ${detail.description}'.toText14(weight: FontWeight.w500), + // ); + // }).toList(), + // SizedBox(height: 16.h), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // SizedBox(), + // CustomButton( + // icon: AppAssets.view_report_icon, + // iconColor: AppColors.primaryRedColor, + // iconSize: 16.h, + // text: LocaleKeys.viewReport.tr(context: context), + // onPressed: () {}, + // backgroundColor: AppColors.secondaryLightRedColor, + // borderColor: AppColors.secondaryLightRedColor, + // textColor: AppColors.primaryRedColor, + // fontSize: 14, + // fontWeight: FontWeight.bold, + // borderRadius: 12, + // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + // height: 40.h, + // ), + // ], + // ), + // ], + // ), + // ) + // : SizedBox.shrink(key: ValueKey(-index)), + // ), ], ), ), diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 9358d00..dd80513 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -79,7 +79,7 @@ class _MedicalFilePageState extends State { insuranceViewModel.initInsuranceProvider(); medicalFileViewModel.setIsPatientSickLeaveListLoading(true); medicalFileViewModel.getPatientSickLeaveList(); - medicalFileViewModel.getFamilyFiles(); + // medicalFileViewModel.getFamilyFiles(); medicalFileViewModel.onTabChanged(0); } }); @@ -582,7 +582,7 @@ class _MedicalFilePageState extends State { myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!, width: 64.h, height: 64.h, - fit: BoxFit.fill, + fit: BoxFit.cover, ).circle(100).toShimmer2(isShow: false, radius: 50.h), SizedBox(height: 8.h), Expanded( diff --git a/lib/presentation/medical_file/patient_sickleaves_list_page.dart b/lib/presentation/medical_file/patient_sickleaves_list_page.dart index 215836f..a28f518 100644 --- a/lib/presentation/medical_file/patient_sickleaves_list_page.dart +++ b/lib/presentation/medical_file/patient_sickleaves_list_page.dart @@ -30,10 +30,7 @@ class _PatientSickleavesListPageState extends State { void initState() { scheduleMicrotask(() { medicalFileViewModel.setIsPatientSickLeaveListLoading(true); - medicalFileViewModel.getPatientSickLeaveList(onError: (error) { - Navigator.of(context).pop(); - Navigator.of(context).pop(); - }); + medicalFileViewModel.getPatientSickLeaveList(); }); super.initState(); } @@ -75,6 +72,7 @@ class _PatientSickleavesListPageState extends State { child: PatientSickLeaveCard( patientSickLeavesResponseModel: medicalFileVM.patientSickLeaveList.first, isLoading: false, + isSickLeaveListPage: true, ).paddingSymmetrical(24.h, 0.0), ), ), diff --git a/lib/presentation/medical_file/vaccine_list_page.dart b/lib/presentation/medical_file/vaccine_list_page.dart index 969e3f5..82c9441 100644 --- a/lib/presentation/medical_file/vaccine_list_page.dart +++ b/lib/presentation/medical_file/vaccine_list_page.dart @@ -5,6 +5,7 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/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/medical_file/medical_file_view_model.dart'; @@ -28,10 +29,7 @@ class _VaccineListPageState extends State { void initState() { scheduleMicrotask(() { medicalFileViewModel.setIsPatientVaccineListLoading(true); - medicalFileViewModel.getPatientVaccinesList(onError: (error) { - Navigator.of(context).pop(); - Navigator.of(context).pop(); - }); + medicalFileViewModel.getPatientVaccinesList(); }); super.initState(); } @@ -51,7 +49,11 @@ class _VaccineListPageState extends State { SizedBox(height: 16.h), ListView.separated( scrollDirection: Axis.vertical, - itemCount: medicalFileVM.isPatientVaccineListLoading ? 5 : medicalFileVM.patientVaccineList.length, + itemCount: medicalFileVM.isPatientVaccineListLoading + ? 5 + : medicalFileVM.patientVaccineList.isNotEmpty + ? medicalFileVM.patientVaccineList.length + : 1, shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.only(left: 24.h, right: 24.h), @@ -102,8 +104,9 @@ class _VaccineListPageState extends State { ), ), ) - : AnimationConfiguration.staggeredList( - position: index, + : medicalFileVM.patientVaccineList.isNotEmpty + ? AnimationConfiguration.staggeredList( + position: index, duration: const Duration(milliseconds: 1000), child: SlideAnimation( verticalOffset: 100.0, @@ -166,7 +169,8 @@ class _VaccineListPageState extends State { ), ), ), - ); + ) + : Utils.getNoDataWidget(context, noDataText: "No vaccines data found...".needTranslation); }, separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), ), diff --git a/lib/presentation/medical_file/widgets/lab_rad_card.dart b/lib/presentation/medical_file/widgets/lab_rad_card.dart index 766ef6d..e396d62 100644 --- a/lib/presentation/medical_file/widgets/lab_rad_card.dart +++ b/lib/presentation/medical_file/widgets/lab_rad_card.dart @@ -54,7 +54,7 @@ class LabRadCard extends StatelessWidget { SizedBox.shrink(), Transform.flip( flipX: appState.isArabic() ? true : false, - child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor) + child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon_small, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor) .toShimmer2(isShow: false, radius: 12.h), ), ], diff --git a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart index 1f6c0bf..8fc3805 100644 --- a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart +++ b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart @@ -23,11 +23,12 @@ import 'package:open_filex/open_filex.dart'; import 'package:provider/provider.dart'; class PatientSickLeaveCard extends StatelessWidget { - PatientSickLeaveCard({super.key, required this.patientSickLeavesResponseModel, this.isLoading = false}); + PatientSickLeaveCard({super.key, required this.patientSickLeavesResponseModel, this.isLoading = false, this.isSickLeaveListPage = false}); late MedicalFileViewModel medicalFileViewModel; PatientSickLeavesResponseModel patientSickLeavesResponseModel; bool isLoading; + bool isSickLeaveListPage = false; @override Widget build(BuildContext context) { @@ -128,7 +129,7 @@ class PatientSickLeaveCard extends StatelessWidget { ).toShimmer2(isShow: isLoading), ), SizedBox(width: 8.h), - Expanded( + isSickLeaveListPage ? SizedBox.shrink() : Expanded( flex: 1, child: Container( height: 40.h, diff --git a/lib/widgets/in_app_browser/InAppBrowser.dart b/lib/widgets/in_app_browser/InAppBrowser.dart index 3fe0de9..e8370e5 100644 --- a/lib/widgets/in_app_browser/InAppBrowser.dart +++ b/lib/widgets/in_app_browser/InAppBrowser.dart @@ -219,12 +219,12 @@ class MyInAppBrowser extends InAppBrowser { // service.tamaraInsertRequest(tamaraRequestModel, context).then((res) { // // if (context != null) GifLoaderDialogUtils.hideDialog(context); - // generateTamaraURL(amount, orderDesc, transactionID, projId, emailId, paymentMethod, patientType, patientName, patientID, authenticatedUser, isLiveCareAppo, servID, LiveServID, appoDate, - // appoNo, clinicID, doctorID, "", installments) - // .then((value) { - // paymentType = _PAYMENT_TYPE.PATIENT; - // this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(value))), options: _InAppBrowserOptions); - // }); + generateTamaraURL(amount, orderDesc, transactionID, projId, emailId, paymentMethod, patientType, patientName, patientID, authenticatedUser, isLiveCareAppo, servID, LiveServID, appoDate, appoNo, + clinicID, doctorID, "", installments) + .then((value) { + paymentType = _PAYMENT_TYPE.PATIENT; + this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(value))), options: _InAppBrowserOptions); + }); // }).catchError((err) { // print(err); // // if (context != null) GifLoaderDialogUtils.hideDialog(context); @@ -333,12 +333,12 @@ class MyInAppBrowser extends InAppBrowser { form = form.replaceFirst('PROJECT_ID_VALUE', projId); form = form.replaceFirst('PAYMENT_OPTION_VALUE', paymentMethod); form = form.replaceFirst('LANG_VALUE', currentLanguageID); - form = form.replaceFirst('SERVICE_URL_VALUE', "https://mdlaboratories.com/tamaralive/Home/Checkout"); + form = form.replaceFirst('SERVICE_URL_VALUE', ApiConsts.TAMARA_URL); - form = form.replaceFirst('INSTALLMENTS_VALUE', installments); + form = form.replaceFirst('INSTALLMENTS_VALUE', "3"); form = form.replaceFirst('CUSTNATIONALID_VALUE', authUser.patientIdentificationNo!); form = form.replaceFirst('CUSTMOBILE_VALUE', authUser.mobileNumber!); - form = form.replaceFirst('CUSTDOB_VALUE', DateUtil.getDayMonthYearDateFormatted(authUser.strDateofBirth!)); + form = form.replaceFirst('CUSTDOB_VALUE', DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(authUser.dateofBirth!))); form = form.replaceFirst('CURRENCY_VALUE', authUser.outSa == 0 ? "SAR" : "AED"); form = form.replaceFirst('COUNTRY_CODE_VALUE', authUser.outSa == 0 ? "966" : "971"); -- 2.30.2 From 903248133eef8f2af1f86b81e205dbddfa3ae8ec Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Sun, 5 Oct 2025 13:49:58 +0300 Subject: [PATCH 2/5] Lab order & results enhancements --- lib/core/api/api_client.dart | 2 +- lib/core/api_consts.dart | 2 +- .../book_appointment_page.dart | 107 +++++++++++++++++- .../book_appointment/select_clinic_page.dart | 1 + .../select_livecare_clinic_page.dart | 3 - lib/presentation/lab/lab_order_by_test.dart | 56 ++++----- lib/presentation/lab/lab_orders_page.dart | 2 +- .../lab/lab_result_item_view.dart | 6 +- 8 files changed, 138 insertions(+), 41 deletions(-) diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 2efeeed..35d63b4 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -174,7 +174,7 @@ class ApiClientImp implements ApiClient { } // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 1018977; + // body['PatientID'] = 4767477; // body['PatientTypeID'] = 1; // // body['PatientOutSA'] = 0; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 9e1a9ba..8225796 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -727,7 +727,7 @@ const FAMILY_FILES= 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatu 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/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index 516aff9..b124438 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -9,6 +9,7 @@ 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/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; @@ -22,6 +23,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_ import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.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/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; @@ -43,9 +45,11 @@ class _BookAppointmentPageState extends State { late AppointmentViaRegionViewmodel regionalViewModel; late BookAppointmentsViewModel bookAppointmentsViewModel; late ImmediateLiveCareViewModel immediateLiveCareViewModel; + late final AuthenticationViewModel authVM; @override void initState() { + authVM = context.read(); scheduleMicrotask(() { bookAppointmentsViewModel.selectedTabIndex = 0; bookAppointmentsViewModel.initBookAppointmentViewModel(); @@ -204,11 +208,12 @@ class _BookAppointmentPageState extends State { ).paddingSymmetrical(24.h, 0.h); case 1: //TODO: Get LiveCare type Select UI from Hussain - return Column( - children: [ - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, + return appState.isAuthenticated + ? Column( + children: [ + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: false, ), @@ -318,7 +323,8 @@ class _BookAppointmentPageState extends State { ), ), ], - ).paddingSymmetrical(24.h, 0.h); + ).paddingSymmetrical(24.h, 0.h) + : getLiveCareNotLoggedInUI(); default: SizedBox.shrink(); } @@ -375,4 +381,93 @@ class _BookAppointmentPageState extends State { }); } } + + Widget getLiveCareNotLoggedInUI() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.immediate_service_icon, width: 58.h, height: 58.h), + SizedBox(width: 18.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Immediate service".needTranslation.toText18(color: AppColors.textColor, isBold: true), + "No need to wait, you will get medical consultation immediately via video call".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + ], + ), + ), + ], + ), + SizedBox(height: 24.h), + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.no_visit_icon, width: 58.h, height: 58.h), + SizedBox(width: 18.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "No visit required".needTranslation.toText18(color: AppColors.textColor, isBold: true), + LocaleKeys.livecarePoint5.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + ], + ), + ), + ], + ), + SizedBox(height: 24.h), + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.doctor_contact_icon, width: 58.h, height: 58.h), + SizedBox(width: 18.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Doctor will contact".needTranslation.toText18(color: AppColors.textColor, isBold: true), + "A specialised doctor will contact you and will be able to view your medical history".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + ], + ), + ), + ], + ), + SizedBox(height: 24.h), + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.free_med_delivery_icon, width: 58.h, height: 58.h), + SizedBox(width: 18.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Free medicine delivery".needTranslation.toText18(color: AppColors.textColor, isBold: true), + "Offers free medicine delivery for the LiveCare appointment".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + ], + ), + ), + ], + ), + SizedBox(height: 36.h), + CustomButton( + text: "Login to use this service".needTranslation, + onPressed: () async { + await authVM.onLoginPressed(); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + fontSize: 16, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 50.h, + icon: AppAssets.login1, + iconColor: AppColors.whiteColor, + iconSize: 24.h, + ), + ], + ).paddingSymmetrical(24.h, 0.h); + } } diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index 36a490d..869fea3 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -215,6 +215,7 @@ class _SelectClinicPageState extends State { } } + // ClinicID 17 & Clinic ID 253 void handleDoctorScreen() { if (widget.isFromRegionFlow) { Navigator.of(context).push( diff --git a/lib/presentation/book_appointment/select_livecare_clinic_page.dart b/lib/presentation/book_appointment/select_livecare_clinic_page.dart index 1e40357..12a99bc 100644 --- a/lib/presentation/book_appointment/select_livecare_clinic_page.dart +++ b/lib/presentation/book_appointment/select_livecare_clinic_page.dart @@ -6,12 +6,9 @@ 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/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.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/routes/custom_page_route.dart'; -import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; class SelectLivecareClinicPage extends StatelessWidget { diff --git a/lib/presentation/lab/lab_order_by_test.dart b/lib/presentation/lab/lab_order_by_test.dart index bb0f391..4c0d2c3 100644 --- a/lib/presentation/lab/lab_order_by_test.dart +++ b/lib/presentation/lab/lab_order_by_test.dart @@ -35,41 +35,43 @@ class LabOrderByTest extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // ...labOrder!.testDetails!.map((detail) { - Padding( - padding: EdgeInsets.only(bottom: 8.h), - child: '${tests!.description}'.toText14(weight: FontWeight.w500), - ), - - SizedBox(height: 12.h), - + '${tests!.description}'.toText16(isBold: true), + SizedBox(height: 4.h), + //TODO: Add test long description from the Lab Order API + "Measurement of ALT (alanine aminotransferase) level in blood used to assess liver functions".toText12(fontWeight: FontWeight.w500), + SizedBox(height: 8.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ AppCustomChipWidget( - richText: '${"Last Tested:".needTranslation} ${ DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false)}'.toText12(isBold: true), - // chipType: ChipTypeEnum.lightBg, + richText: '${"Last Tested:".needTranslation} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false)}'.toText12(fontWeight: FontWeight.w500), backgroundColor: AppColors.greyLightColor, textColor: AppColors.textColor, - // borderRadius: 5, - ), - CustomButton( - icon: AppAssets.view_report_icon, - iconColor: AppColors.primaryRedColor, - iconSize: 16.h, - text: LocaleKeys.viewReport.tr(context: context), - onPressed: () { + ], + ), + SizedBox(height: 16.h), + Row( + children: [ + Expanded(child: Container()), + Expanded( + child: CustomButton( + icon: AppAssets.view_report_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + text: LocaleKeys.viewReport.tr(context: context), + onPressed: () { onTap(); - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 14, - fontWeight: FontWeight.bold, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), ), ], ), diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index 1c78bbd..6b65d5d 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -1 +1 @@ -import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/LabResultByHospital.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import '../../widgets/appbar/collapsing_list_view.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late LabRangeViewModel rangeViewModel; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context); rangeViewModel = Provider.of(context); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: LocaleKeys.labResults.tr(), search: () async { final lavVM = Provider.of(context, listen: false); if (lavVM.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; lavVM.filterLabReports(value); } } }, child: SingleChildScrollView( padding: EdgeInsets.all(24.h), physics: NeverScrollableScrollPhysics(), child: Consumer( builder: (context, model, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16.h), CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, "By Visit".needTranslation), CustomTabBarModel(null, "By Test".needTranslation), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), SizedBox(height: 16.h), selectedFilterText!.isNotEmpty ? CustomChipWidget( chipText: selectedFilterText!, chipType: ChipTypeEnum.alert, isSelected: true, ) : SizedBox(), activeIndex == 0 ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.isNotEmpty ? model.patientLabOrders.length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.patientLabOrders.isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabResultItemView( onTap: () { model.currentlySelectedPatientOrder = model.patientLabOrders[ index]; scheduleMicrotask(() { labProvider .getPatientLabResultByHospital( model.patientLabOrders[ index]); labProvider .getPatientSpecialResult( model.patientLabOrders[ index]); }); Navigator.push(context,FadePage( page: LabResultByHospitals(), )); }, labOrder: model.patientLabOrders[index], index: index, isExpanded: isExpanded)), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) : ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.uniqueTests.toList().length, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabOrderByTest( onTap: () { if(model.uniqueTests.toList()[index].model != null) { rangeViewModel.flush(); model.getPatientLabResult( model.uniqueTests .toList()[index] .model!, model.uniqueTests .toList()[index].description!); } }, tests: model.uniqueTests.toList()[index], index: index, isExpanded: isExpanded)), ), ); }, ) ], ); }, ), ), )); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file +import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/LabResultByHospital.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import '../../widgets/appbar/collapsing_list_view.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late LabRangeViewModel rangeViewModel; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context); rangeViewModel = Provider.of(context); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: LocaleKeys.labResults.tr(), search: () async { final lavVM = Provider.of(context, listen: false); if (lavVM.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; lavVM.filterLabReports(value); } } }, child: SingleChildScrollView( padding: EdgeInsets.all(24.h), physics: NeverScrollableScrollPhysics(), child: Consumer( builder: (context, model, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16.h), CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, "By Visit".needTranslation), CustomTabBarModel(null, "By Test".needTranslation), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), SizedBox(height: 16.h), selectedFilterText!.isNotEmpty ? CustomChipWidget( chipText: selectedFilterText!, chipType: ChipTypeEnum.alert, isSelected: true, ) : SizedBox(), activeIndex == 0 ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.isNotEmpty ? model.patientLabOrders.length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.patientLabOrders.isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabResultItemView( onTap: () { model.currentlySelectedPatientOrder = model.patientLabOrders[ index]; scheduleMicrotask(() { labProvider .getPatientLabResultByHospital( model.patientLabOrders[ index]); labProvider .getPatientSpecialResult( model.patientLabOrders[ index]); }); Navigator.push( context, CustomPageRoute( page: LabResultByHospitals(), )); }, labOrder: model.patientLabOrders[index], index: index, isExpanded: isExpanded)), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) : ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.uniqueTests.toList().length, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabOrderByTest( onTap: () { if(model.uniqueTests.toList()[index].model != null) { rangeViewModel.flush(); model.getPatientLabResult( model.uniqueTests .toList()[index] .model!, model.uniqueTests .toList()[index].description!); } }, tests: model.uniqueTests.toList()[index], index: index, isExpanded: isExpanded)), ), ); }, ) ], ); }, ), ), )); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file diff --git a/lib/presentation/lab/lab_result_item_view.dart b/lib/presentation/lab/lab_result_item_view.dart index 57a10ae..9269c6d 100644 --- a/lib/presentation/lab/lab_result_item_view.dart +++ b/lib/presentation/lab/lab_result_item_view.dart @@ -81,8 +81,10 @@ class LabResultItemView extends StatelessWidget { AppCustomChipWidget(labelText: isLoading ? "null" : labOrder!.clinicDescription!).toShimmer2(isShow: isLoading, width: 100), ], ), - Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon_small, + isLoading + ? SizedBox.shrink() + : Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon_small, iconColor: AppColors.textColor, width: 20.h, height: 14.h, -- 2.30.2 From 415c0eb3c72897bd40f11a796524ef6791da2013 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Sun, 5 Oct 2025 17:16:25 +0300 Subject: [PATCH 3/5] Dental appointment booking implementation contd. --- lib/core/api/api_client.dart | 2 +- lib/core/api_consts.dart | 25 ++-- lib/core/utils/utils.dart | 6 +- .../book_appointments_repo.dart | 44 ++++++ .../book_appointments_view_model.dart | 42 +++++- ...et_patient_dental_plan_response_model.dart | 40 ++++++ .../book_appointment/select_clinic_page.dart | 125 +++++++++++++++++- .../book_appointment/select_doctor_page.dart | 103 ++++++++------- lib/presentation/home/landing_page.dart | 4 +- 9 files changed, 317 insertions(+), 74 deletions(-) create mode 100644 lib/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 35d63b4..9140a13 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -174,7 +174,7 @@ class ApiClientImp implements ApiClient { } // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 4767477; + // body['PatientID'] = 4767884; // body['PatientTypeID'] = 1; // // body['PatientOutSA'] = 0; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 8225796..511cb37 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -746,6 +746,8 @@ class ApiConsts { static String TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout"; static String GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments"; + // static String GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; + // var payFortEnvironment = FortEnvironment.test; // var applePayMerchantId = "merchant.com.hmgwebservices.uat"; @@ -764,42 +766,41 @@ class ApiConsts { payFortEnvironment = FortEnvironment.test; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; - TAMARA_URL = "https://mdlaboratories.com/tamara/Home/Checkout"; - GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamara/Home/GetInstallments"; + TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; + GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; break; case AppEnvironmentTypeEnum.uat: baseUrl = "https://uat.hmgwebservices.com/"; payFortEnvironment = FortEnvironment.test; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; - TAMARA_URL = "https://mdlaboratories.com/tamara/Home/Checkout"; - // GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamara/Home/GetInstallments"; - GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments"; + TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; + GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; break; case AppEnvironmentTypeEnum.preProd: - + baseUrl = "https://webservices.hmg.com/"; payFortEnvironment = FortEnvironment.production; applePayMerchantId = "merchant.com.hmgwebservices"; SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; - TAMARA_URL = "https://mdlaboratories.com/tamara/Home/Checkout"; - GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamara/Home/GetInstallments"; + TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; + GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; break; case AppEnvironmentTypeEnum.qa: baseUrl = "https://uat.hmgwebservices.com/"; payFortEnvironment = FortEnvironment.test; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; - TAMARA_URL = "https://mdlaboratories.com/tamara/Home/Checkout"; - GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamara/Home/GetInstallments"; + TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; + GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; break; case AppEnvironmentTypeEnum.staging: baseUrl = "https://uat.hmgwebservices.com/"; payFortEnvironment = FortEnvironment.test; applePayMerchantId = "merchant.com.hmgwebservices.uat"; SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; - TAMARA_URL = "https://mdlaboratories.com/tamara/Home/Checkout"; - GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamara/Home/GetInstallments"; + TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; + GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; break; } } diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index f3a53b9..2bff2ac 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -323,7 +323,7 @@ class Utils { children: [ Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), SizedBox(height: 8.h), - (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor), + (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true), SizedBox(height: 8.h), ], ).center; @@ -355,7 +355,7 @@ class Utils { ).center; } - static Widget getWarningWidget({String? loadingText, bool isShowActionButtons = false, Function? onConfirmTap, Function? onCancelTap}) { + static Widget getWarningWidget({String? loadingText, bool isShowActionButtons = false, Widget? bodyWidget, Function? onConfirmTap, Function? onCancelTap}) { return Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, @@ -364,6 +364,8 @@ class Utils { SizedBox(height: 8.h), (loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor, letterSpacing: 0), SizedBox(height: 16.h), + bodyWidget ?? SizedBox.shrink(), + SizedBox(height: 16.h), isShowActionButtons ? Row( children: [ diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart index 43a7eb3..f15c5ea 100644 --- a/lib/features/book_appointments/book_appointments_repo.dart +++ b/lib/features/book_appointments/book_appointments_repo.dart @@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/resp_model 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_livecare_clinics_response_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_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/services/logger_service.dart'; @@ -68,6 +69,9 @@ abstract class BookAppointmentsRepo { required int serviceID, Function(dynamic)? onSuccess, Function(String)? onError}); + + Future>>> getPatientDentalEstimation( + {required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}); } class BookAppointmentsRepoImp implements BookAppointmentsRepo { @@ -656,4 +660,44 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> getPatientDentalEstimation( + {required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async { + Map mapDevice = { + "ProjectID": projectID, + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + HAS_DENTAL_PLAN, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + onError!(error); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['List_IsPatientHasOnGoingEstimation']; + final estimationList = list.map((item) => PatientDentalPlanEstimationResponseModel.fromJson(item as Map)).toList().cast(); + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: estimationList, + ); + } 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/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index ad59a27..f04394b 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -15,6 +15,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/doctor_profile_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'; import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart'; @@ -55,6 +56,10 @@ class BookAppointmentsViewModel extends ChangeNotifier { List liveCareDoctorsList = []; + List patientDentalPlanEstimationList = []; + int totalTimeNeededForDentalProcedure = 0; + bool isContinueDentalPlan = false; + GetClinicsListResponseModel selectedClinic = GetClinicsListResponseModel(); DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel(); GetLiveCareClinicsResponseModel selectedLiveCareClinic = GetLiveCareClinicsResponseModel(); @@ -128,6 +133,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { clinicsList.clear(); doctorsList.clear(); liveCareClinicsList.clear(); + patientDentalPlanEstimationList.clear(); + isContinueDentalPlan = false; // getLocation(); notifyListeners(); } @@ -275,11 +282,10 @@ class BookAppointmentsViewModel extends ChangeNotifier { } //TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName - Future getDoctorsList( - {int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { + Future getDoctorsList({int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async { doctorsList.clear(); projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID; - final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName); + final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName, isContinueDentalPlan: isContinueDentalPlan); result.fold( (failure) async { @@ -901,4 +907,34 @@ class BookAppointmentsViewModel extends ChangeNotifier { filteredDoctorList = getDoctorListAsPerSelection(); notifyListeners(); } + + Future getPatientDentalEstimation({required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async { + patientDentalPlanEstimationList.clear(); + totalTimeNeededForDentalProcedure = 0; + isContinueDentalPlan = false; + notifyListeners(); + + final result = await bookAppointmentsRepo.getPatientDentalEstimation(projectID: projectID); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + onError!(apiResponse.errorMessage!); + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientDentalPlanEstimationList = apiResponse.data!; + + patientDentalPlanEstimationList.forEach((v) { + totalTimeNeededForDentalProcedure += (v.neededTime ?? 0); + }); + + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } } diff --git a/lib/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart b/lib/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart new file mode 100644 index 0000000..6b90199 --- /dev/null +++ b/lib/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart @@ -0,0 +1,40 @@ +class PatientDentalPlanEstimationResponseModel { + dynamic setupID; + dynamic estimationNo; + int? projectID; + String? procedureId; + int? patientID; + int? sequenceNo; + int? neededTime; + String? procedureName; + String? procedureNameN; + + PatientDentalPlanEstimationResponseModel( + {this.setupID, this.estimationNo, this.projectID, this.procedureId, this.patientID, this.sequenceNo, this.neededTime, this.procedureName, this.procedureNameN}); + + PatientDentalPlanEstimationResponseModel.fromJson(Map json) { + setupID = json['SetupID']; + estimationNo = json['EstimationNo']; + projectID = json['ProjectID']; + procedureId = json['ProcedureId']; + patientID = json['PatientID']; + sequenceNo = json['sequenceNo']; + neededTime = json['NeededTime']; + procedureName = json['ProcedureName']; + procedureNameN = json['ProcedureNameN']; + } + + Map toJson() { + final Map data = new Map(); + data['SetupID'] = this.setupID; + data['EstimationNo'] = this.estimationNo; + data['ProjectID'] = this.projectID; + data['ProcedureId'] = this.procedureId; + data['PatientID'] = this.patientID; + data['sequenceNo'] = this.sequenceNo; + data['NeededTime'] = this.neededTime; + data['ProcedureName'] = this.procedureName; + data['ProcedureNameN'] = this.procedureNameN; + return data; + } +} diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index e1bbf1f..18e6b4b 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -23,8 +23,11 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/select_livecar import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/clinic_card.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/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; @@ -216,16 +219,124 @@ class _SelectClinicPageState extends State { } //17 and 235 - void handleDoctorScreen(GetClinicsListResponseModel clinic) { + void handleDoctorScreen(GetClinicsListResponseModel clinic) async { if (widget.isFromRegionFlow) { - Navigator.of(context).push( - CustomPageRoute( - page: SelectDoctorPage(), - ), - ); + //Dental Clinic Flow + if (clinic.clinicID == 17) { + LoaderBottomSheet.showLoader(loadingText: "Checking for an existing dental plan, Please wait...".needTranslation); + await bookAppointmentsViewModel.getPatientDentalEstimation(projectID: int.parse(bookAppointmentsViewModel.currentlySelectedHospitalFromRegionFlow ?? "0")).then((value) { + LoaderBottomSheet.hideLoader(); + if (bookAppointmentsViewModel.patientDentalPlanEstimationList.isNotEmpty) { + showCommonBottomSheetWithoutHeight( + // title: LocaleKeys.notice.tr(context: context), + title: "Dental treatment plan".needTranslation, + context, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "You have an existing treatment plan: ".needTranslation.toText14(weight: FontWeight.w500), + SizedBox(height: 8.h), + Container( + width: double.infinity, + padding: EdgeInsets.all(16.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.separated( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: bookAppointmentsViewModel.patientDentalPlanEstimationList.length, + separatorBuilder: (_, __) { + return Column( + children: [ + SizedBox(height: 8.h), + Divider(height: 1, color: AppColors.greyColor), + SizedBox(height: 8.h), + ], + ); + }, + itemBuilder: (context, index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + bookAppointmentsViewModel.patientDentalPlanEstimationList[index].procedureName!.toText12(isBold: true), + AppCustomChipWidget(icon: AppAssets.appointment_time_icon, labelText: "${bookAppointmentsViewModel.totalTimeNeededForDentalProcedure} Mins".needTranslation), + ], + ); + }, + ), + SizedBox( + height: 16.h, + ), + Divider(height: 1, color: AppColors.greyColor), + SizedBox( + height: 8.h, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Total time required".needTranslation.toText14(isBold: true), + AppCustomChipWidget(icon: AppAssets.appointment_time_icon, labelText: "30 Mins".needTranslation), + ], + ) + ], + ), + ), + SizedBox(height: 16.h), + "Would you like to continue it?".needTranslation.toText14(weight: FontWeight.w500), + SizedBox(height: 16.h), + Row( + children: [ + Expanded( + child: CustomButton( + text: LocaleKeys.cancel.tr(), + onPressed: () { + Navigator.of(context).pop(); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + icon: AppAssets.cancel, + iconColor: AppColors.whiteColor, + ), + ), + SizedBox(width: 8.h), + Expanded( + child: CustomButton( + text: LocaleKeys.confirm.tr(), + onPressed: () async {}, + backgroundColor: AppColors.bgGreenColor, + borderColor: AppColors.bgGreenColor, + textColor: Colors.white, + icon: AppAssets.confirm, + ), + ), + ], + ) + ], + ), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } else { + // Navigate to Chief Complaint Screen + } + }); + } else { + Navigator.of(context).push( + CustomPageRoute( + page: SelectDoctorPage(), + ), + ); + } } else { var bottomSheetType = RegionBottomSheetType.FOR_CLINIIC; - print("the cliininc id is ${clinic.clinicID}"); if (clinic.clinicID == 17 || clinic.clinicID == 235) { bottomSheetType = RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER; } diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart index 5975631..8d24b43 100644 --- a/lib/presentation/book_appointment/select_doctor_page.dart +++ b/lib/presentation/book_appointment/select_doctor_page.dart @@ -89,11 +89,7 @@ class _SelectDoctorPageState extends State { bookAppointmentsViewModel.filterClinics(""); textFocusNode.unfocus(); }, - child: Utils.buildSvgWithAssets( - icon: AppAssets.close_bottom_sheet_icon, - width: 20.h, - height: 20.h, - fit: BoxFit.scaleDown), + child: Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown), ) : null, onChange: (value) { @@ -111,8 +107,11 @@ class _SelectDoctorPageState extends State { padding: EdgeInsets.only(top: 24.h), shrinkWrap: true, physics: NeverScrollableScrollPhysics(), - itemCount: - bookAppointmentsVM.isDoctorsListLoading ? 5 : (bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList.length : bookAppointmentsVM.doctorsList.length), + itemCount: bookAppointmentsVM.isDoctorsListLoading + ? 5 + : (bookAppointmentsVM.isLiveCareSchedule + ? (bookAppointmentsVM.liveCareDoctorsList.isNotEmpty ? bookAppointmentsVM.liveCareDoctorsList.length : 1) + : (bookAppointmentsVM.doctorsList.isNotEmpty ? bookAppointmentsVM.doctorsList.length : 1)), itemBuilder: (context, index) { return bookAppointmentsVM.isDoctorsListLoading ? DoctorCard( @@ -120,47 +119,49 @@ class _SelectDoctorPageState extends State { isLoading: true, bookAppointmentsViewModel: bookAppointmentsViewModel, ) - : 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: DoctorCard( - doctorsListResponseModel: bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index], - isLoading: false, - bookAppointmentsViewModel: bookAppointmentsViewModel, - ).onPress(() async { - bookAppointmentsVM - .setSelectedDoctor(bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index]); - // bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel()); - LoaderBottomSheet.showLoader(); - await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) { - LoaderBottomSheet.hideLoader(); - Navigator.of(context).push( - CustomPageRoute( - page: DoctorProfilePage(), - ), - ); - }, onError: (err) { - LoaderBottomSheet.hideLoader(); - showCommonBottomSheetWithoutHeight( - context, - child: Utils.getErrorWidget(loadingText: err), - callBackFunc: () {}, - isFullScreen: false, - isCloseButtonVisible: true, - ); - }); - }), + : checkIsDoctorsListEmpty() + ? Utils.getNoDataWidget(context, noDataText: "No Doctor found for selected criteria...".needTranslation) + : 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: DoctorCard( + doctorsListResponseModel: bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index], + isLoading: false, + bookAppointmentsViewModel: bookAppointmentsViewModel, + ).onPress(() async { + bookAppointmentsVM + .setSelectedDoctor(bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index]); + // bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel()); + LoaderBottomSheet.showLoader(); + await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) { + LoaderBottomSheet.hideLoader(); + Navigator.of(context).push( + CustomPageRoute( + page: DoctorProfilePage(), + ), + ); + }, onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }); + }), + ), + ), ), - ), - ), - ); + ); }, separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), ), @@ -173,4 +174,12 @@ class _SelectDoctorPageState extends State { ), ); } + + bool checkIsDoctorsListEmpty() { + if (bookAppointmentsViewModel.isLiveCareSchedule) { + return bookAppointmentsViewModel.liveCareDoctorsList.isEmpty; + } else { + return bookAppointmentsViewModel.doctorsList.isEmpty; + } + } } diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index f28beed..db8a068 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -135,8 +135,8 @@ class _LandingPageState extends State { fontSize: 14, fontWeight: FontWeight.w500, borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40, + padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), + height: 40.h, ), Row( mainAxisSize: MainAxisSize.min, -- 2.30.2 From 2b21086cc17d210d91d4b6b22237d859ab0f8906 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Mon, 6 Oct 2025 10:19:53 +0300 Subject: [PATCH 4/5] Dental appointment booking flow implementation contd. --- lib/core/api/api_client.dart | 4 ++-- .../book_appointments_view_model.dart | 5 +++++ lib/features/lab/lab_view_model.dart | 20 ++++++++++++------- .../patient_lab_orders_response_model.dart | 9 +++++++-- .../book_appointment/select_clinic_page.dart | 10 +++++++++- lib/presentation/lab/lab_order_by_test.dart | 7 ++++--- lib/presentation/lab/lab_orders_page.dart | 2 +- .../LabResultList.dart | 4 ++-- .../lab_order_result_item.dart | 11 ++-------- .../lab/lab_results/lab_result_details.dart | 3 ++- 10 files changed, 47 insertions(+), 28 deletions(-) diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 9140a13..cd7b673 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -173,8 +173,8 @@ class ApiClientImp implements ApiClient { body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken; } - // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 4767884; + body['TokenID'] = "@dm!n"; + body['PatientID'] = 4767884; // body['PatientTypeID'] = 1; // // body['PatientOutSA'] = 0; diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index f04394b..5e0d424 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -196,6 +196,11 @@ class BookAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } + setIsContinueDentalPlan(bool value) { + isContinueDentalPlan = value; + notifyListeners(); + } + void onTabChanged(int index) { selectedTabIndex = index; notifyListeners(); diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart index 1bbc27c..e7507b0 100644 --- a/lib/features/lab/lab_view_model.dart +++ b/lib/features/lab/lab_view_model.dart @@ -58,7 +58,7 @@ class LabViewModel extends ChangeNotifier { List get labSuggestions => _labSuggestionsList; - Set uniqueTests = {}; + Set uniqueTests = {}; double maxY = 0.0; double maxX = double.infinity; @@ -78,6 +78,11 @@ class LabViewModel extends ChangeNotifier { } Future getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { + patientLabOrders.clear(); + uniqueTests.clear(); + uniqueTests = {}; + notifyListeners(); + final result = await labRepo.getPatientLabOrders(); result.fold( @@ -132,11 +137,12 @@ class LabViewModel extends ChangeNotifier { } getUniqueTestDescription() { - - uniqueTests = { + uniqueTests = { for (var item in patientLabOrders) if (item.testDetails != null) ...?item.testDetails?.map((test) => TestDetails( + testDescriptionEn: test.testDescriptionEn.toString(), + testDescriptionAr: test.testDescriptionAr.toString(), description: test.description.toString(), testCode: test.testCode.toString(), testID: test.testID, @@ -170,8 +176,7 @@ class LabViewModel extends ChangeNotifier { ); } - Future getPatientLabResult( - PatientLabOrdersResponseModel laborder, String procedureName) async { + Future getPatientLabResult(PatientLabOrdersResponseModel laborder, String procedureName, String testDescription) async { LoaderBottomSheet.showLoader(); mainLabResults.clear(); filteredGraphValues.clear(); @@ -223,8 +228,9 @@ class LabViewModel extends ChangeNotifier { recentResult.verifiedOn = resultDate(DateUtil.convertStringToDate(recentResult.verifiedOnDateTime!)); // filteredGraphValues = [filteredGraphValues.first]; navigationService.push(MaterialPageRoute( - builder: (_) => - LabResultDetails(recentLabResult: recentResult))); + builder: (_) => LabResultDetails(recentLabResult: recentResult, testDescription: testDescription), + ), + ); notifyListeners(); } }, diff --git a/lib/features/lab/models/resp_models/patient_lab_orders_response_model.dart b/lib/features/lab/models/resp_models/patient_lab_orders_response_model.dart index aadfc76..4143498 100644 --- a/lib/features/lab/models/resp_models/patient_lab_orders_response_model.dart +++ b/lib/features/lab/models/resp_models/patient_lab_orders_response_model.dart @@ -224,21 +224,26 @@ class PatientLabOrdersResponseModel { class TestDetails { String? description; + String? testDescriptionEn; + String? testDescriptionAr; String? testCode; String? testID; String? createdOn; PatientLabOrdersResponseModel? model; - TestDetails({this.description, this.testCode, this.testID, this.createdOn, this.model}); + + TestDetails({this.description, this.testDescriptionEn, this.testDescriptionAr, this.testCode, this.testID, this.createdOn, this.model}); TestDetails.fromJson(Map json) { description = json['Description']; + testDescriptionEn = json['TestDescriptionEn'] ?? ""; + testDescriptionAr = json['TestDescriptionAr'] ?? ""; testCode = json['TestCode']; testID = json['TestID']; createdOn = json['CreatedOn']; } Map toJson() { - final Map data = new Map(); + final Map data = {}; data['Description'] = this.description; data['TestCode'] = this.testCode; data['TestID'] = this.testID; diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index 18e6b4b..3326ef5 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -296,6 +296,7 @@ class _SelectClinicPageState extends State { child: CustomButton( text: LocaleKeys.cancel.tr(), onPressed: () { + bookAppointmentsViewModel.setIsContinueDentalPlan(false); Navigator.of(context).pop(); }, backgroundColor: AppColors.primaryRedColor, @@ -309,7 +310,14 @@ class _SelectClinicPageState extends State { Expanded( child: CustomButton( text: LocaleKeys.confirm.tr(), - onPressed: () async {}, + onPressed: () async { + bookAppointmentsViewModel.setIsContinueDentalPlan(true); + Navigator.of(context).push( + CustomPageRoute( + page: SelectDoctorPage(), + ), + ); + }, backgroundColor: AppColors.bgGreenColor, borderColor: AppColors.bgGreenColor, textColor: Colors.white, diff --git a/lib/presentation/lab/lab_order_by_test.dart b/lib/presentation/lab/lab_order_by_test.dart index 4c0d2c3..2b791ae 100644 --- a/lib/presentation/lab/lab_order_by_test.dart +++ b/lib/presentation/lab/lab_order_by_test.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; @@ -19,8 +20,9 @@ class LabOrderByTest extends StatelessWidget { final TestDetails? tests; final bool isLoading; final bool isExpanded; + final AppState appState; - const LabOrderByTest({super.key, required this.onTap, this.tests, required this.index, this.isLoading = false, this.isExpanded = false}); + const LabOrderByTest({super.key, required this.onTap, required this.appState, this.tests, required this.index, this.isLoading = false, this.isExpanded = false}); @override build(BuildContext context) { @@ -37,8 +39,7 @@ class LabOrderByTest extends StatelessWidget { children: [ '${tests!.description}'.toText16(isBold: true), SizedBox(height: 4.h), - //TODO: Add test long description from the Lab Order API - "Measurement of ALT (alanine aminotransferase) level in blood used to assess liver functions".toText12(fontWeight: FontWeight.w500), + (appState.isArabic() ? tests!.testDescriptionAr : tests!.testDescriptionEn)!.toText12(fontWeight: FontWeight.w500), SizedBox(height: 8.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index 6b65d5d..6311451 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -1 +1 @@ -import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/LabResultByHospital.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import '../../widgets/appbar/collapsing_list_view.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late LabRangeViewModel rangeViewModel; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context); rangeViewModel = Provider.of(context); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: LocaleKeys.labResults.tr(), search: () async { final lavVM = Provider.of(context, listen: false); if (lavVM.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; lavVM.filterLabReports(value); } } }, child: SingleChildScrollView( padding: EdgeInsets.all(24.h), physics: NeverScrollableScrollPhysics(), child: Consumer( builder: (context, model, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16.h), CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, "By Visit".needTranslation), CustomTabBarModel(null, "By Test".needTranslation), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), SizedBox(height: 16.h), selectedFilterText!.isNotEmpty ? CustomChipWidget( chipText: selectedFilterText!, chipType: ChipTypeEnum.alert, isSelected: true, ) : SizedBox(), activeIndex == 0 ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.isNotEmpty ? model.patientLabOrders.length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.patientLabOrders.isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabResultItemView( onTap: () { model.currentlySelectedPatientOrder = model.patientLabOrders[ index]; scheduleMicrotask(() { labProvider .getPatientLabResultByHospital( model.patientLabOrders[ index]); labProvider .getPatientSpecialResult( model.patientLabOrders[ index]); }); Navigator.push( context, CustomPageRoute( page: LabResultByHospitals(), )); }, labOrder: model.patientLabOrders[index], index: index, isExpanded: isExpanded)), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) : ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.uniqueTests.toList().length, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabOrderByTest( onTap: () { if(model.uniqueTests.toList()[index].model != null) { rangeViewModel.flush(); model.getPatientLabResult( model.uniqueTests .toList()[index] .model!, model.uniqueTests .toList()[index].description!); } }, tests: model.uniqueTests.toList()[index], index: index, isExpanded: isExpanded)), ), ); }, ) ], ); }, ), ), )); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file +import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.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/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/LabResultByHospital.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import '../../widgets/appbar/collapsing_list_view.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late LabRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context); rangeViewModel = Provider.of(context); _appState = getIt(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: LocaleKeys.labResults.tr(), search: () async { final lavVM = Provider.of(context, listen: false); if (lavVM.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; lavVM.filterLabReports(value); } } }, child: SingleChildScrollView( padding: EdgeInsets.all(24.h), physics: NeverScrollableScrollPhysics(), child: Consumer( builder: (context, model, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16.h), CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, "By Visit".needTranslation), CustomTabBarModel(null, "By Test".needTranslation), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), SizedBox(height: 16.h), selectedFilterText!.isNotEmpty ? CustomChipWidget( chipText: selectedFilterText!, chipType: ChipTypeEnum.alert, isSelected: true, ) : SizedBox(), activeIndex == 0 ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.isNotEmpty ? model.patientLabOrders.length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.patientLabOrders.isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabResultItemView( onTap: () { model.currentlySelectedPatientOrder = model.patientLabOrders[ index]; labProvider.getPatientLabResultByHospital(model.patientLabOrders[ index]); labProvider .getPatientSpecialResult( model.patientLabOrders[ index]); Navigator.push( context, CustomPageRoute( page: LabResultByHospitals(), )); }, labOrder: model.patientLabOrders[index], index: index, isExpanded: isExpanded), ), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) : ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.uniqueTests.toList().isNotEmpty ? model.uniqueTests.toList().length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.uniqueTests.toList().isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabOrderByTest( appState: _appState, onTap: () { if (model.uniqueTests.toList()[index].model != null) { rangeViewModel.flush(); model.getPatientLabResult(model.uniqueTests.toList()[index].model!, model.uniqueTests.toList()[index].description!, (_appState.isArabic() ? model.uniqueTests.toList()[index].testDescriptionAr! : model.uniqueTests.toList()[index].testDescriptionEn!)); } }, tests: model.uniqueTests.toList()[index], index: index, isExpanded: isExpanded)), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) ], ); }, ), ), )); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file diff --git a/lib/presentation/lab/lab_result_via_hospital/LabResultList.dart b/lib/presentation/lab/lab_result_via_hospital/LabResultList.dart index 9e1f08c..d7642c2 100644 --- a/lib/presentation/lab/lab_result_via_hospital/LabResultList.dart +++ b/lib/presentation/lab/lab_result_via_hospital/LabResultList.dart @@ -26,8 +26,8 @@ class LabResultList extends StatelessWidget { shrinkWrap: true,itemCount: list.length,itemBuilder: (____, index) { var labItem = list[index]; return LabOrderResultItem(onTap: () { - model.getPatientLabResult(model.currentlySelectedPatientOrder!, labItem.description??""); - }, + model.getPatientLabResult(model.currentlySelectedPatientOrder!, labItem.description ?? "", labItem.packageShortDescription!); + }, tests: labItem, index: index, iconColor: model.getColor(labItem.calculatedResultFlag ?? "N"), diff --git a/lib/presentation/lab/lab_result_via_hospital/lab_order_result_item.dart b/lib/presentation/lab/lab_result_via_hospital/lab_order_result_item.dart index 59638de..7dc4075 100644 --- a/lib/presentation/lab/lab_result_via_hospital/lab_order_result_item.dart +++ b/lib/presentation/lab/lab_result_via_hospital/lab_order_result_item.dart @@ -54,14 +54,7 @@ class LabOrderResultItem extends StatelessWidget { spacing: 6.h, crossAxisAlignment: CrossAxisAlignment.start, children: [ - - Text(severityText.tr(), - style: TextStyle( - fontFamily: 'Poppins', - fontSize: 10.fSize, - fontWeight: FontWeight.w500, - color: AppColors.greyTextColor - )), + severityText.tr().toText10(weight: FontWeight.w500, color: AppColors.greyTextColor), Utils.buildSvgWithAssets( icon: AppAssets.lab_result_indicator, width: 21, @@ -82,7 +75,7 @@ class LabOrderResultItem extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w500, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, diff --git a/lib/presentation/lab/lab_results/lab_result_details.dart b/lib/presentation/lab/lab_results/lab_result_details.dart index 790199a..4b31d0a 100644 --- a/lib/presentation/lab/lab_results/lab_result_details.dart +++ b/lib/presentation/lab/lab_results/lab_result_details.dart @@ -25,8 +25,9 @@ class LabResultDetails extends StatelessWidget { // final List graphPoint; late LabViewModel model; + String? testDescription; - LabResultDetails({super.key, required this.recentLabResult}); + LabResultDetails({super.key, required this.recentLabResult, required this.testDescription}); @override Widget build(BuildContext context) { -- 2.30.2 From b05e4e2a159f1b91d3d24a5e19bc02034c78608d Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Mon, 6 Oct 2025 10:30:04 +0300 Subject: [PATCH 5/5] Dental appointment booking flow implementation contd. --- .../book_appointments/book_appointments_repo.dart | 5 +++-- .../book_appointments/book_appointments_view_model.dart | 9 ++++++++- .../book_appointment/review_appointment_page.dart | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart index f15c5ea..82ca921 100644 --- a/lib/features/book_appointments/book_appointments_repo.dart +++ b/lib/features/book_appointments/book_appointments_repo.dart @@ -24,7 +24,7 @@ abstract class BookAppointmentsRepo { Future>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}); - Future>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, {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}); Future>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}); @@ -229,7 +229,7 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo { //TODO: Implement the logic for Dental & laser clinics @override Future>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, - {Function(dynamic)? onSuccess, Function(String)? onError}) async { + {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { Map mapDevice = { "DoctorID": doctorId, "IsBookingForLiveCare": isBookingForLiveCare, @@ -238,6 +238,7 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo { "OriginalClinicID": clinicID, "days": 0, "isReschadual": false, + "ContinueDentalPlan": continueDentalPlan }; try { diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index 5e0d424..a6c741f 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -378,7 +378,14 @@ class BookAppointmentsViewModel extends ChangeNotifier { final DateFormat dateFormatter = DateFormat('yyyy-MM-dd'); Map _eventsParsed; - final result = await bookAppointmentsRepo.getDoctorFreeSlots(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare, onError: onError); + final result = await bookAppointmentsRepo.getDoctorFreeSlots( + selectedDoctor.clinicID ?? 0, + selectedDoctor.projectID ?? 0, + selectedDoctor.doctorID ?? 0, + isBookingForLiveCare, + continueDentalPlan: isContinueDentalPlan, + onError: onError, + ); result.fold( (failure) async { diff --git a/lib/presentation/book_appointment/review_appointment_page.dart b/lib/presentation/book_appointment/review_appointment_page.dart index 280c4ad..77cd249 100644 --- a/lib/presentation/book_appointment/review_appointment_page.dart +++ b/lib/presentation/book_appointment/review_appointment_page.dart @@ -229,6 +229,7 @@ class _ReviewAppointmentPageState extends State { }); }); } else { + //TODO: Add patient Derma package check API Here await bookAppointmentsViewModel.insertSpecificAppointment(onError: (err) { print(err.data["ErrorEndUserMessage"]); LoadingUtils.hideFullScreenLoader(); -- 2.30.2