diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ffa3a908..cd5030d2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -92,7 +92,7 @@ - + diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index 0f33138a..4c7c41e2 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -566,6 +566,8 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { final appointmentsList = (list as List).map((item) => GetFavoriteDoctorsListModel.fromJson(item as Map)).toList().cast(); + appointmentsList.removeWhere((element) => element.isActive == false); + apiResponse = GenericApiModel>( messageStatus: messageStatus, statusCode: statusCode, diff --git a/lib/features/notifications/notifications_view_model.dart b/lib/features/notifications/notifications_view_model.dart index ea552292..85049ec9 100644 --- a/lib/features/notifications/notifications_view_model.dart +++ b/lib/features/notifications/notifications_view_model.dart @@ -15,7 +15,7 @@ class NotificationsViewModel extends ChangeNotifier { List notificationsList = []; int currentPage = 0; - int pagingSize = 15; + int pagingSize = 50; int notificationStatusID = 2; // Default to status 2 (e.g., unread) NotificationsViewModel({ diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index d762f795..e755cfeb 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -258,7 +258,8 @@ class _AppointmentPaymentPageState extends State { CustomPageRoute( page: InsuranceHomePage(), ), - ); + ) + .then((val) {}); }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.secondaryLightRedBorderColor, diff --git a/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart b/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart index fb641327..c01899cb 100644 --- a/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart +++ b/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart @@ -1,5 +1,6 @@ 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/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; @@ -8,7 +9,9 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/laser_body import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/laser_body_parts.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:provider/provider.dart'; +import 'package:smooth_corner/smooth_corner.dart'; class BodyPartsListing extends StatelessWidget { final List parts; @@ -49,11 +52,7 @@ class BodyPartsListing extends StatelessWidget { Visibility( visible: !isLoading, child: GridView.builder( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - childAspectRatio: 85 / 107, - crossAxisSpacing: 4.h, - mainAxisSpacing: 21.h), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 75 / 107, crossAxisSpacing: 4.h, mainAxisSpacing: 21.h), physics: NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: parts.length, @@ -73,7 +72,7 @@ class BodyPartsListing extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ AspectRatio( - aspectRatio: 130 / 120, + aspectRatio: 150 / 140, child: FittedBox( fit: BoxFit.fitWidth, child: Stack( @@ -112,14 +111,41 @@ class BodyPartsListing extends StatelessWidget { child: Center( child: Column( children: [ - context.read().getLaserProcedureNameWRTLanguage(parts[index]).toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - "${LocaleKeys.mins.tr()}: ".toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2), - parts[index].timeDuration!.toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2), - ], - ), + context.read().getLaserProcedureNameWRTLanguage(parts[index]).toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 1), + SizedBox(height: 4.h), + AppCustomChipWidget( + backgroundColor: _isSelected ? AppColors.chipSecondaryLightRedColor : AppColors.whiteColor, + textColor: _isSelected ? AppColors.chipPrimaryRedBorderColor : AppColors.blackColor, + // labelText: "${parts[index].timeDuration!} ${LocaleKeys.mins.tr()}", + richText: "${parts[index].timeDuration!} ${LocaleKeys.mins.tr()}".toText12( + fontWeight: FontWeight.w500, + color: _isSelected ? AppColors.chipPrimaryRedBorderColor : AppColors.blackColor, + ), + icon: AppAssets.waiting_time_clock, + iconHasColor: true, + iconColor: _isSelected ? AppColors.chipPrimaryRedBorderColor : AppColors.blackColor, + iconSize: 16, + labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w), + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.w), + deleteIconSize: Size(18.w, 18.h), + shape: SmoothRectangleBorder( + borderRadius: BorderRadius.circular(10.r), + smoothness: 10, + side: BorderSide(color: _isSelected ? AppColors.chipPrimaryRedBorderColor : AppColors.borderGrayColor, width: 1), + )), + + // AppCustomChipWidget( + // labelText: "${parts[index].timeDuration!} ${LocaleKeys.mins.tr()}", + // backgroundColor: AppColors.whiteColor, + // // textColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), + // ), + // Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // "${LocaleKeys.mins.tr()}: ".toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2), + // parts[index].timeDuration!.toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2), + // ], + // ), ], ), ), diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart index c885b595..673532ac 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart @@ -171,7 +171,22 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { CustomPageRoute( page: InsuranceHomePage(), ), - ); + ) + .then((val) async { + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingFeesPleaseWait.tr()); + await immediateLiveCareVM.getLiveCareImmediateAppointmentFees(onSuccess: (val) { + LoaderBottomSheet.hideLoader(); + }, onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }); + }); }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.secondaryLightRedBorderColor, @@ -343,15 +358,17 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { bool cameraGranted = statuses[Permission.camera]?.isGranted ?? false; bool micGranted = statuses[Permission.microphone]?.isGranted ?? false; bool notifGranted = statuses[Permission.notification]?.isGranted ?? false; + bool alertWindowGranted = Platform.isAndroid ? (statuses[Permission.systemAlertWindow]?.isGranted ?? false) : true; // If all required permissions are already granted - if (cameraGranted && micGranted && notifGranted) return true; + if (cameraGranted && micGranted && notifGranted && alertWindowGranted) return true; // Collect only the missing permissions final missing = []; if (!cameraGranted) missing.add(Permission.camera); if (!micGranted) missing.add(Permission.microphone); if (!notifGranted) missing.add(Permission.notification); + if (Platform.isAndroid && !alertWindowGranted) missing.add(Permission.systemAlertWindow); // If any of the missing permissions are permanently denied/restricted -> open settings (single dialog) final permanent = missing.where((p) => (statuses[p]?.isPermanentlyDenied ?? false) || (statuses[p]?.isRestricted ?? false)).toList(); @@ -392,6 +409,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { cameraGranted = newStatuses[Permission.camera]?.isGranted ?? cameraGranted; micGranted = newStatuses[Permission.microphone]?.isGranted ?? micGranted; notifGranted = newStatuses[Permission.notification]?.isGranted ?? notifGranted; + alertWindowGranted = newStatuses[Permission.systemAlertWindow]?.isGranted ?? alertWindowGranted; // If any requested permission is now permanently denied -> open settings final newlyPermanent = missing.where((p) => (newStatuses[p]?.isPermanentlyDenied ?? false) || (newStatuses[p]?.isRestricted ?? false)).toList(); @@ -407,7 +425,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { return false; } - return cameraGranted && micGranted && notifGranted; + return cameraGranted && micGranted && notifGranted && alertWindowGranted; } // Future askVideoCallPermission() async { diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index bce87ca2..32525234 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -199,6 +199,7 @@ class _LandingPageState extends State { Navigator.of(context).push( CustomPageRoute( + direction: AxisDirection.down, page: FamilyMedicalScreen(), ), ); diff --git a/lib/presentation/home/widgets/welcome_widget.dart b/lib/presentation/home/widgets/welcome_widget.dart index b05aeb99..b2d4e3a1 100644 --- a/lib/presentation/home/widgets/welcome_widget.dart +++ b/lib/presentation/home/widgets/welcome_widget.dart @@ -45,6 +45,7 @@ class WelcomeWidget extends StatelessWidget { getIt.get().getProfileSettings(); Navigator.of(context).push( CustomPageRoute( + direction: getIt.get().isArabic() ? AxisDirection.right : AxisDirection.left, page: ProfileSettings(), ), ); diff --git a/lib/presentation/insurance/widgets/insurance_update_details_card.dart b/lib/presentation/insurance/widgets/insurance_update_details_card.dart index e7cfe059..ae1eaf51 100644 --- a/lib/presentation/insurance/widgets/insurance_update_details_card.dart +++ b/lib/presentation/insurance/widgets/insurance_update_details_card.dart @@ -12,6 +12,7 @@ import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.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'; @@ -121,17 +122,27 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget { insuranceCardImage: "", onSuccess: (val) { LoaderBottomSheet.hideLoader(); - showCommonBottomSheetWithoutHeight( - title: LocaleKeys.success.tr(context: context), - context, - child: Utils.getSuccessWidget(loadingText: LocaleKeys.insuranceRequestSubmittedSuccessfully.tr(context: context)), - callBackFunc: () { - Navigator.pop(context); - }, - isFullScreen: false, - isCloseButtonVisible: false, - isAutoDismiss: true - ); + getIt.get().showCommonBottomSheetWithoutH( + message: LocaleKeys.insuranceRequestSubmittedSuccessfully.tr(context: context), + label: LocaleKeys.notice.tr(), + onOkPressed: () { + Navigator.pop(context); + }, + okLabel: "confirm", + cancelLabel: LocaleKeys.acknowledged.tr(context: context), + isConfirmButton: true, + ); + // showCommonBottomSheetWithoutHeight( + // title: LocaleKeys.success.tr(context: context), + // context, + // child: Utils.getSuccessWidget(loadingText: LocaleKeys.insuranceRequestSubmittedSuccessfully.tr(context: context)), + // callBackFunc: () { + // Navigator.pop(context); + // }, + // isFullScreen: false, + // isCloseButtonVisible: false, + // isAutoDismiss: true + // ); // Future.delayed(Duration(milliseconds: 2000)).then((value) async { // Navigator.pop(context); // }); diff --git a/lib/presentation/my_family/my_family.dart b/lib/presentation/my_family/my_family.dart index dca75747..66b31cfc 100644 --- a/lib/presentation/my_family/my_family.dart +++ b/lib/presentation/my_family/my_family.dart @@ -43,6 +43,7 @@ class _FamilyMedicalScreenState extends State { AppState appState = getIt.get(); return CollapsingListView( + isClose: true, title: LocaleKeys.familyTitle.tr(context: context), bottomChild: appState.getAuthenticatedUser()!.isParentUser! ? Container( diff --git a/lib/presentation/notifications/notifications_list_page.dart b/lib/presentation/notifications/notifications_list_page.dart index bc90b4aa..99201688 100644 --- a/lib/presentation/notifications/notifications_list_page.dart +++ b/lib/presentation/notifications/notifications_list_page.dart @@ -216,18 +216,18 @@ class _NotificationsListPageState extends State { }).paddingSymmetrical(16.w, 0.h), ).paddingSymmetrical(24.w, 0.h), SizedBox(height: 16.h), - SizedBox( - key: _bottomKey, - child: "Show more notifications".toText16( - color: AppColors.primaryRedColor, - isBold: true, - isUnderLine: true - ), - ).onPress(() async { - await notificationsVM.loadMoreNotifications(); - _scrollToBottom(); - }), - SizedBox(height: 24.h), + // SizedBox( + // key: _bottomKey, + // child: "Show more notifications".toText16( + // color: AppColors.primaryRedColor, + // isBold: true, + // isUnderLine: true + // ), + // ).onPress(() async { + // await notificationsVM.loadMoreNotifications(); + // _scrollToBottom(); + // }), + SizedBox(height: 24.h), ], ); }), diff --git a/lib/presentation/radiology/radiology_orders_page.dart b/lib/presentation/radiology/radiology_orders_page.dart index 1a3ab448..db2e4f52 100644 --- a/lib/presentation/radiology/radiology_orders_page.dart +++ b/lib/presentation/radiology/radiology_orders_page.dart @@ -11,7 +11,6 @@ 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_view_model.dart'; -import 'package:hmg_patient_app_new/features/radiology/models/resp_models/patient_radiology_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/radiology/radiology_result_page.dart'; diff --git a/lib/presentation/radiology/radiology_result_page.dart b/lib/presentation/radiology/radiology_result_page.dart index 05f23530..8fbdd8e6 100644 --- a/lib/presentation/radiology/radiology_result_page.dart +++ b/lib/presentation/radiology/radiology_result_page.dart @@ -158,28 +158,64 @@ class _RadiologyResultPageState extends State { borderRadius: 24.h, hasShadow: true, ), - child: widget.patientRadiologyResponseModel.dIAPACSURL != "" ? CustomButton( - text: LocaleKeys.openRad.tr(context: context), - onPressed: () async { - if (radiologyViewModel.radiologyImageURL.isNotEmpty) { - Uri uri = Uri.parse(radiologyViewModel.radiologyImageURL); - launchUrl(uri, mode: LaunchMode.platformDefault, webOnlyWindowName: ""); - } else { - Utils.showToast("Radiology image not available"); - } - }, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 45.h, - icon: AppAssets.imageIcon, - iconColor: Colors.white, - iconSize: 20.h, - ).paddingSymmetrical(24.h, 24.h) : SizedBox.shrink(), + child: Column( + children: [ + SizedBox(height: 24.h), + widget.patientRadiologyResponseModel.dIAPACSURL != "" + ? CustomButton( + text: LocaleKeys.openRad.tr(context: context), + onPressed: () async { + if (radiologyViewModel.radiologyImageURL.isNotEmpty) { + Uri uri = Uri.parse(radiologyViewModel.radiologyImageURL); + launchUrl(uri, mode: LaunchMode.platformDefault, webOnlyWindowName: ""); + } else { + Utils.showToast("Radiology image not available"); + } + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 56.h, + icon: AppAssets.imageIcon, + iconColor: Colors.white, + iconSize: 20.h, + ).paddingSymmetrical(24.h, 0.h) + : SizedBox.shrink(), + Container( + height: 56.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.r), + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + stops: [0.236, 1.0], // 53.6% and 100% + colors: [ + Color(0xFF8A38F5), // Transparent + Color(0xFFE20BBB), // Solid #F8F8F8 + ], + ), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(right: 4.w, left: 4.w), + child: Utils.buildSvgWithAssets(icon: AppAssets.aiOverView, width: 16.h, height: 16.h, iconColor: Colors.white), + ), + LocaleKeys.generateAiAnalysisResult.tr(context: context).toText16(isBold: true, color: Colors.white) + ], + ), + ).paddingSymmetrical(24.h, 12.h).onPress(() { + // Need to implement the Radiology AI analysis result generation functionality here + Utils.showToast("Radiology AI analysis result generation functionality is not implemented yet"); + }), + ], + ), ), ], ), diff --git a/lib/services/livecare_permission_service.dart b/lib/services/livecare_permission_service.dart index a9a5d294..b8d71ff1 100644 --- a/lib/services/livecare_permission_service.dart +++ b/lib/services/livecare_permission_service.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; @@ -29,6 +30,7 @@ class LiveCarePermissionService { Permission.camera, Permission.microphone, Permission.notification, + if (Platform.isAndroid) Permission.systemAlertWindow, ]; try { @@ -87,6 +89,7 @@ class LiveCarePermissionService { if (permission == Permission.camera) return 'Camera'; if (permission == Permission.microphone) return 'Microphone'; if (permission == Permission.notification) return 'Notifications'; + if (permission == Permission.systemAlertWindow) return 'Display over other apps'; return permission.toString(); } }