diff --git a/assets/images/svg/patient_qr_code_icon.svg b/assets/images/svg/patient_qr_code_icon.svg new file mode 100644 index 00000000..c76f0326 --- /dev/null +++ b/assets/images/svg/patient_qr_code_icon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index d0321ace..b2bc5c2d 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -1959,12 +1959,14 @@ "refund": "استرداد", "parkingQR": "رمز الاستجابة السريعة لوقوف السيارات", "parkingQRDesc": "امسح رمز الاستجابة السريعة (QR Code) أدناه عند بوابة الخروج من موقف السيارات:", - "livecareUpdateInsuranceSent": "تم استلام طلب تحديث بيانات التأمين الخاص بك، ويجري العمل عليه حالياً. سنقوم بإشعارك فور اكتمال التحديث.", - "livecareUpdateInsurance": "سيتم تحويل طلب تحديث بيانات التأمين إلى فريق LiveCare المختص لاستكمال الإجراءات. هل تود المتابعة؟", - "schedule": "جدول", - "doctorSchedule": "جدول الطبيب", - "weeklyShift": "الفترات الأسبوعية", - "dayOff": "إجازة", - "notAvailable": "غير متاح", - "hide": "إخفاء" + "livecareUpdateInsuranceSent": "تم استلام طلب تحديث بيانات التأمين الخاص بك، ويجري العمل عليه حالياً. سنقوم بإشعارك فور اكتمال التحديث.", + "livecareUpdateInsurance": "سيتم تحويل طلب تحديث بيانات التأمين إلى فريق LiveCare المختص لاستكمال الإجراءات. هل تود المتابعة؟", + "schedule": "جدول", + "doctorSchedule": "جدول الطبيب", + "weeklyShift": "الفترات الأسبوعية", + "dayOff": "إجازة", + "notAvailable": "غير متاح", + "hide": "إخفاء", + "viewPatientQR": "عرض رمز الاستجابة السريعة للمريض", + "patientQR": "رمز الاستجابة السريعة للمريض" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index ae344b01..0e3429e4 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -1949,14 +1949,16 @@ "invoiceHistory": "Invoice History", "parkingQR": "Parking QR Code", "parkingQRDesc": "Scan the below QR Code at the exit parking gate:", - "livecareUpdateInsuranceSent": "Your insurance update request is being processed. You will be notified once the update is completed.", - "livecareUpdateInsurance": "Please note that your insurance update request will be sent to the our LiveCare team for update. Would you like to proceed?", - "schedule": "Schedule", - "doctorSchedule": "Doctor Schedule", - "weeklyShift": "Weekly Shift", - "dayOff": "Off", - "notAvailable": "Not Available", - "hide": "Hide" + "livecareUpdateInsuranceSent": "Your insurance update request is being processed. You will be notified once the update is completed.", + "livecareUpdateInsurance": "Please note that your insurance update request will be sent to the our LiveCare team for update. Would you like to proceed?", + "schedule": "Schedule", + "doctorSchedule": "Doctor Schedule", + "weeklyShift": "Weekly Shift", + "dayOff": "Off", + "notAvailable": "Not Available", + "hide": "Hide", + "viewPatientQR": "View Patient QR Code", + "patientQR": "Patient QR Code" } diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index aba22eb3..b4611890 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -399,6 +399,7 @@ class AppAssets { static const String habibLogoOnly = '$svgBasePath/habib logo_only.svg'; static const String descriptionIcon = '$svgBasePath/description_icon.svg'; static const String selectImageIcon = '$svgBasePath/select_image_icon.svg'; + static const String patientQRCodeIcon = '$svgBasePath/patient_qr_code_icon.svg'; // PNGS // static const String hmgLogo = '$pngBasePath/hmg_logo.png'; diff --git a/lib/features/insurance/insurance_view_model.dart b/lib/features/insurance/insurance_view_model.dart index 621ebe4c..2422920a 100644 --- a/lib/features/insurance/insurance_view_model.dart +++ b/lib/features/insurance/insurance_view_model.dart @@ -33,14 +33,10 @@ class InsuranceViewModel extends ChangeNotifier { InsuranceViewModel({required this.insuranceRepo, required this.errorHandlerService}); initInsuranceProvider() { - debugPrint("InsuranceViewModel: initInsuranceProvider called, isInsuranceDataToBeLoaded=$isInsuranceDataToBeLoaded"); if (isInsuranceDataToBeLoaded) { patientInsuranceList.clear(); isInsuranceLoading = true; - debugPrint("InsuranceViewModel: Calling getPatientInsuranceDetails API"); getPatientInsuranceDetails(); - } else { - debugPrint("InsuranceViewModel: Skipping API call, isInsuranceDataToBeLoaded is false"); } patientInsuranceCardHistoryList.clear(); isInsuranceHistoryLoading = true; @@ -82,17 +78,14 @@ class InsuranceViewModel extends ChangeNotifier { Future getPatientInsuranceDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async { if (!isInsuranceDataToBeLoaded) { - debugPrint("InsuranceViewModel: getPatientInsuranceDetails - skipping, flag is false"); return; } - debugPrint("InsuranceViewModel: getPatientInsuranceDetails - making API call"); final result = await insuranceRepo.getPatientInsuranceDetails(); result.fold( // (failure) async => await errorHandlerService.handleError(failure: failure), (failure) async { - debugPrint("InsuranceViewModel: API call failed - ${failure.toString()}"); isInsuranceLoading = false; isInsuranceDataToBeLoaded = true; isInsuranceExpired = false; @@ -102,11 +95,9 @@ class InsuranceViewModel extends ChangeNotifier { }, (apiResponse) { if (apiResponse.messageStatus == 2) { - debugPrint("InsuranceViewModel: API returned error status"); isInsuranceDataToBeLoaded = false; // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { - debugPrint("InsuranceViewModel: API success - received ${apiResponse.data?.length ?? 0} insurance records"); patientInsuranceList = apiResponse.data!; isInsuranceLoading = false; isInsuranceDataToBeLoaded = false; @@ -115,7 +106,6 @@ class InsuranceViewModel extends ChangeNotifier { isInsuranceExpired = DateTime.now().isAfter( DateUtil.convertStringToDate(patientInsuranceList.first.cardValidTo), ); - debugPrint("InsuranceViewModel: Insurance card expired: $isInsuranceExpired"); } isInsuranceExpiryBannerShown = isInsuranceExpired; diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index 87484e06..d7a926d3 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -172,9 +172,9 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { try { final list = response['OnlineCheckInAppointments']; patientShareObj = (list == null || list.isEmpty) ? PatientAppointmentShareResponseModel() : PatientAppointmentShareResponseModel.fromJson(list[0]); - patientShareObj.isCash = response["IsCash"]; - patientShareObj.isEligible = response["IsEligible"]; - patientShareObj.isInsured = response["IsInsured"]; + patientShareObj.isCash = isLiveCareAppointment ? list[0]["IsCash"] : response["IsCash"]; + patientShareObj.isEligible = isLiveCareAppointment ? list[0]["IsEligible"] : response["IsEligible"]; + patientShareObj.isInsured = isLiveCareAppointment ? list[0]["IsInsured"] : response["IsInsured"]; // if (list == null || list.isEmpty) { // throw Exception("patient share list is empty"); diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index b480a354..52d8b240 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -277,7 +277,6 @@ class MyAppointmentsViewModel extends ChangeNotifier { } if (!isAppointmentDataToBeLoaded) return; - debugPrint('getPatientAppointments CALLED!!!'); patientAppointmentsByClinic.clear(); patientAppointmentsByHospital.clear(); @@ -347,9 +346,6 @@ class MyAppointmentsViewModel extends ChangeNotifier { } // } - debugPrint('Upcoming Appointments: ${patientUpcomingAppointmentsHistoryList.length}'); - debugPrint('Arrived Appointments: ${patientArrivedAppointmentsHistoryList.length}'); - debugPrint('All Appointments: ${patientAppointmentsHistoryList.length}'); getFiltersForSelectedAppointmentList(filteredAppointmentList); notifyListeners(); } @@ -1299,7 +1295,6 @@ class MyAppointmentsViewModel extends ChangeNotifier { ); } catch (e) { // Ignore errors if reminder doesn't exist - debugPrint("No existing ${type.displayName} to remove"); } } } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 80cea8b4..51a687a8 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1948,5 +1948,7 @@ abstract class LocaleKeys { static const weeklyShift = 'weeklyShift'; static const dayOff = 'dayOff'; static const hide = 'hide'; + static const viewPatientQR = 'viewPatientQR'; + static const patientQR = 'patientQR'; } diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index 55769fbe..9b210eee 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -67,7 +67,7 @@ class _BookAppointmentPageState extends State { immediateLiveCareViewModel.initImmediateLiveCare(); if (appState.isAuthenticated) { getIt.get().getPatientMyDoctors(isTop8: true); - getIt.get().getPatientFavouriteDoctors(); + // getIt.get().getPatientFavouriteDoctors(); } }); WidgetsBinding.instance.addPostFrameCallback((_) { diff --git a/lib/presentation/insurance/insurance_approval_details_page.dart b/lib/presentation/insurance/insurance_approval_details_page.dart index b1fce84a..d0d7f681 100644 --- a/lib/presentation/insurance/insurance_approval_details_page.dart +++ b/lib/presentation/insurance/insurance_approval_details_page.dart @@ -102,7 +102,7 @@ class InsuranceApprovalDetailsPage extends StatelessWidget { spacing: 3.h, runSpacing: 4.h, children: [ - AppCustomChipWidget(labelText: insuranceApprovalResponseModel.clinicName!), + AppCustomChipWidget(labelText: insuranceApprovalResponseModel.clinicName ?? ""), AppCustomChipWidget(labelText: "${LocaleKeys.approvalNo.tr(context: context)} ${insuranceApprovalResponseModel.approvalNo}", isEnglishOnly: true), AppCustomChipWidget(labelText: "${LocaleKeys.unusedCount.tr(context: context)} ${insuranceApprovalResponseModel.unUsedCount}", isEnglishOnly: true), AppCustomChipWidget(labelText: "${LocaleKeys.companyName.tr(context: context)} ${insuranceApprovalResponseModel.companyName}"), diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index b382bf01..cdee10db 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -110,16 +110,19 @@ class _MedicalFilePageState extends State { appState = getIt.get(); scheduleMicrotask(() { if (appState.isAuthenticated) { - myAppointmentsViewModel.getPatientMyDoctors(isTop8: true); - prescriptionsViewModel.initPrescriptionsViewModel(); insuranceViewModel.initInsuranceProvider(); - medicalFileViewModel.setIsPatientSickLeaveListLoading(true); - medicalFileViewModel.getPatientSickLeaveList(); - medicalFileViewModel.onTabChanged(0); - medicalFileViewModel.setIsPatientMedicalReportsLoading(true); - medicalFileViewModel.getPatientMedicalReportList(); // Load vital signs hmgServicesViewModel.getPatientVitalSign(); + + // myAppointmentsViewModel.getPatientMyDoctors(isTop8: true); + // prescriptionsViewModel.initPrescriptionsViewModel(); + + // medicalFileViewModel.setIsPatientSickLeaveListLoading(true); + // medicalFileViewModel.getPatientSickLeaveList(); + // medicalFileViewModel.setIsPatientMedicalReportsLoading(true); + // medicalFileViewModel.getPatientMedicalReportList(); + + medicalFileViewModel.onTabChanged(0); } }); super.initState(); @@ -236,8 +239,8 @@ class _MedicalFilePageState extends State { children: [ SizedBox( width: MediaQuery.of(context).size.width * 0.6, - child: "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" - .toText18(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 2), + child: + "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText18(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 2), ), SizedBox(height: 4.h), Wrap( @@ -481,6 +484,20 @@ class _MedicalFilePageState extends State { expansionMode: ExpansionMode.exactlyOne, dividerColor: AppColors.dividerColor, itemPadding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 14.h), + onItemToggled: (index, isExpanded) { + debugPrint("Item at index $index is now ${isExpanded ? 'expanded' : 'collapsed'}"); + + if (index == 0 && isExpanded) { + myAppointmentsViewModel.getPatientMyDoctors(isTop8: true); + prescriptionsViewModel.initPrescriptionsViewModel(); + } + if (index == 1 && isExpanded) { + medicalFileViewModel.setIsPatientSickLeaveListLoading(true); + medicalFileViewModel.getPatientSickLeaveList(); + medicalFileViewModel.setIsPatientMedicalReportsLoading(true); + medicalFileViewModel.getPatientMedicalReportList(); + } + }, items: [ ExpandableListItem( title: LocaleKeys.medicalServices.tr(context: context).toText18(isBold: true), diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart index 2e6b60b6..2e9bb3b7 100644 --- a/lib/presentation/profile_settings/profile_settings.dart +++ b/lib/presentation/profile_settings/profile_settings.dart @@ -42,6 +42,8 @@ import 'package:in_app_review/in_app_review.dart'; import 'dart:ui' as ui; +import 'widgets/patient_qr_code_widget.dart'; + class ProfileSettings extends StatefulWidget { const ProfileSettings({super.key}); @@ -232,6 +234,17 @@ class ProfileSettingsState extends State { decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false), child: Column( children: [ + actionItem(AppAssets.patientQRCodeIcon, LocaleKeys.viewPatientQR.tr(context: context), () { + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.patientQR.tr(context: context), + context, + child: PatientQrCodeWidget(), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }), + 1.divider, actionItem(AppAssets.language_change, LocaleKeys.language.tr(context: context), () { showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.language.tr(context: context), child: AppLanguageChange(), callBackFunc: () {}, isFullScreen: false); }, trailingLabel: Utils.appState.isArabic() ? "العربية" : "English"), diff --git a/lib/presentation/profile_settings/widgets/patient_qr_code_widget.dart b/lib/presentation/profile_settings/widgets/patient_qr_code_widget.dart new file mode 100644 index 00000000..adc1632e --- /dev/null +++ b/lib/presentation/profile_settings/widgets/patient_qr_code_widget.dart @@ -0,0 +1,56 @@ +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_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.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 '../../../generated/locale_keys.g.dart'; + +class PatientQrCodeWidget extends StatelessWidget { + PatientQrCodeWidget({super.key}); + + late AppState appState; + + @override + Widget build(BuildContext context) { + appState = getIt.get(); + return Column( + children: [ + SizedBox(height: 8.h), + "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText18(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 2), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 4.w, + runSpacing: 6.w, + children: [ + AppCustomChipWidget( + icon: AppAssets.file_icon, + labelText: "${LocaleKeys.fileno.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}", + labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), + ), + AppCustomChipWidget( + icon: AppAssets.checkmark_icon, + labelText: LocaleKeys.verified.tr(context: context), + iconColor: AppColors.successColor, + labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), + ), + ], + ), + SizedBox(height: 16.h), + Image.network( + "https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=${appState.getAuthenticatedUser()!.patientId.toString()}", + fit: BoxFit.fill, + height: 156.h, + width: 156.h, + ), + SizedBox(height: 16.h), + ], + ); + } +}