From 5e81ba1ed8f5dd7738818d0750526f8b52de572e Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Thu, 18 Sep 2025 10:24:13 +0300 Subject: [PATCH 1/4] updates --- .../appointments/my_appointments_page.dart | 1 - .../widgets/appointment_card.dart | 3 +- lib/presentation/home/landing_page.dart | 377 ++++++++++-------- 3 files changed, 210 insertions(+), 171 deletions(-) diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart index 5a3205d..4c16a17 100644 --- a/lib/presentation/appointments/my_appointments_page.dart +++ b/lib/presentation/appointments/my_appointments_page.dart @@ -53,7 +53,6 @@ class _MyAppointmentsPageState extends State { CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { - print(index); myAppointmentsViewModel.onTabChange(index); }, ).paddingSymmetrical(24.h, 0.h), diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 815443e..29694ed 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -20,10 +20,11 @@ import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:smooth_corner/smooth_corner.dart'; class AppointmentCard extends StatefulWidget { - AppointmentCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel}); + AppointmentCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel, this.isLoading = false}); PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; MyAppointmentsViewModel myAppointmentsViewModel; + bool isLoading; @override State createState() => _AppointmentCardState(); diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 9f8ae79..0a5ec2c 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; import 'package:get_it/get_it.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; @@ -18,6 +19,7 @@ import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_mode import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart'; import 'package:hmg_patient_app_new/presentation/authentication/quick_login.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart'; @@ -54,6 +56,8 @@ class _LandingPageState extends State { late PrescriptionsViewModel prescriptionsViewModel; final CacheService cacheService = GetIt.instance(); + final SwiperController _controller = SwiperController(); + @override void initState() { authVM = context.read(); @@ -93,35 +97,32 @@ class _LandingPageState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - appState.isAuthenticated ? WelcomeWidget( - onTap: () { - Navigator.of(context).push( - FadePage( - page: ProfileSettings(), - ), - ); - }, - name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), - imageUrl: appState - .getAuthenticatedUser() - ?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, - ) + onTap: () { + Navigator.of(context).push( + FadePage( + page: ProfileSettings(), + ), + ); + }, + name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), + imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, + ) : CustomButton( - text: LocaleKeys.loginOrRegister.tr(context: context), - onPressed: () async { - await authVM.onLoginPressed(); - }, - backgroundColor: Color(0xffFEE9EA), - borderColor: Color(0xffFEE9EA), - textColor: Color(0xffED1C2B), - fontSize: 16, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 50, - ), + text: LocaleKeys.loginOrRegister.tr(context: context), + onPressed: () async { + await authVM.onLoginPressed(); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 16, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 50, + ), Row( children: [ Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 20, width: 20).onPress(() { @@ -156,156 +157,194 @@ class _LandingPageState extends State { SizedBox(height: 16.h), appState.isAuthenticated ? Column( - children: [ - Container( - width: double.infinity, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24, - ), - child: Padding( - padding: EdgeInsets.all(12.h), - child: Column( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), - SizedBox(height: 12.h), - "You do not have any upcoming appointment. Please book an appointment".toText12(isCenter: true), - SizedBox(height: 12.h), - CustomButton( - text: LocaleKeys.bookAppo.tr(context: context), - onPressed: () { - Navigator.of(context) - .push( - FadePage( - page: BookAppointmentPage(), - ), - ); - }, - backgroundColor: Color(0xffFEE9EA), - borderColor: Color(0xffFEE9EA), - textColor: Color(0xffED1C2B), - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40, - icon: AppAssets.add_icon, - iconColor: AppColors.primaryRedColor, + children: [ + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Appointments & Visits".toText16(isBold: true), + Row( + children: [ + LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], + ), + ], + ).paddingSymmetrical(24.h, 0.h).onPress(() { + Navigator.of(context).push( + FadePage( + page: MyAppointmentsPage(), + ), + ); + }), + SizedBox(height: 12.h), + Swiper( + itemCount: 3, + layout: SwiperLayout.STACK, + loop: true, + itemWidth: MediaQuery.of(context).size.width - 42, + indicatorLayout: PageIndicatorLayout.COLOR, + axisDirection: AxisDirection.right, + controller: _controller, + itemHeight: 210 + 16, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + margin: EdgeInsets.only(top: 210 + 8 + 24), + builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 12.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "Quick Links".toText16(isBold: true), - Row( - children: [ - "View medical file".toText12(color: AppColors.primaryRedColor), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), - ], - ), - ], - ).paddingSymmetrical(24.h, 0.h).onPress(() { - Navigator.of(context).push( - FadePage( - page: MedicalFilePage(), - ), - ); - }), - SizedBox(height: 12.h), - Container( - height: 127.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - children: [ - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getLoggedInServiceCardsList.length, - shrinkWrap: true, - padding: const EdgeInsets.only(left: 0, right: 8), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: SmallServiceCard( - icon: LandingPageData.getLoggedInServiceCardsList[index].icon, - title: LandingPageData.getLoggedInServiceCardsList[index].title, - subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle, - iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor, - textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor, - backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor, - isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold, - serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName, - ), - ), - ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 0.width, + itemBuilder: (BuildContext context, int index) { + return FamilyCardWidget().paddingOnly(right: 16); + }, + ), + // Container( + // width: double.infinity, + // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + // color: AppColors.whiteColor, + // borderRadius: 24, + // ), + // child: Padding( + // padding: EdgeInsets.all(12.h), + // child: Column( + // children: [ + // Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), + // SizedBox(height: 12.h), + // "You do not have any upcoming appointment. Please book an appointment".toText12(isCenter: true), + // SizedBox(height: 12.h), + // CustomButton( + // text: LocaleKeys.bookAppo.tr(context: context), + // onPressed: () { + // Navigator.of(context).push( + // FadePage( + // page: BookAppointmentPage(), + // ), + // ); + // }, + // backgroundColor: Color(0xffFEE9EA), + // borderColor: Color(0xffFEE9EA), + // textColor: Color(0xffED1C2B), + // fontSize: 14, + // fontWeight: FontWeight.w500, + // borderRadius: 12, + // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + // height: 40, + // icon: AppAssets.add_icon, + // iconColor: AppColors.primaryRedColor, + // ), + // ], + // ), + // ), + // ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Quick Links".toText16(isBold: true), + Row( + children: [ + "View medical file".toText12(color: AppColors.primaryRedColor), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], ), + ], + ).paddingSymmetrical(24.h, 0.h).onPress(() { + Navigator.of(context).push( + FadePage( + page: MedicalFilePage(), + ), + ); + }), + SizedBox(height: 12.h), + Container( + height: 127.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24, ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.h), - ], - ) - : Container( - height: 127.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - children: [ - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, - shrinkWrap: true, - padding: const EdgeInsets.only(left: 0, right: 8), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: SmallServiceCard( - icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon, - title: LandingPageData.getNotLoggedInServiceCardsList[index].title, - subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle, - iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor, - textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor, - backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor, - isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold, + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + children: [ + Expanded( + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getLoggedInServiceCardsList.length, + shrinkWrap: true, + padding: const EdgeInsets.only(left: 0, right: 8), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SmallServiceCard( + icon: LandingPageData.getLoggedInServiceCardsList[index].icon, + title: LandingPageData.getLoggedInServiceCardsList[index].title, + subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle, + iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor, + textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor, + backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor, + isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold, + serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName, + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 0.width, ), ), + ], + ), + ), + ).paddingSymmetrical(24.h, 0.h), + ], + ) + : Container( + height: 127.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + children: [ + Expanded( + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, + shrinkWrap: true, + padding: const EdgeInsets.only(left: 0, right: 8), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SmallServiceCard( + icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon, + title: LandingPageData.getNotLoggedInServiceCardsList[index].title, + subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle, + iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor, + textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor, + backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor, + isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold, + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 0.width, ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 0.width, + ), + ], ), ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.h), + ).paddingSymmetrical(24.h, 0.h), SizedBox(height: 16.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -375,7 +414,7 @@ class _LandingPageState extends State { // sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true); authVM.loginWithFingerPrintFace(() { isDone = true; - cacheService.saveBool(key: CacheConst.quickLoginEnabled,value: true); + cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true); setState(() {}); }); }, From 29615834147660be78a0727f06b95176c9f1f9e8 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Thu, 18 Sep 2025 10:49:27 +0300 Subject: [PATCH 2/4] update --- lib/presentation/authentication/register_step2.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart index 8dbe8e8..d58c7b3 100644 --- a/lib/presentation/authentication/register_step2.dart +++ b/lib/presentation/authentication/register_step2.dart @@ -43,7 +43,9 @@ class _RegisterNew extends State { @override Widget build(BuildContext context) { AppState appState = getIt.get(); - var name = appState.getLanguageCode() == "en" + + // TODO: to be checked with yakeen data + var name = authVM!.isUserFromUAE() ? "" : appState.getLanguageCode() == "en" ? ("${appState.getNHICUserData.firstNameEn!.toUpperCase()} ${appState.getNHICUserData.lastNameEn!.toUpperCase()}") : ("${appState.getNHICUserData.firstNameAr!.toUpperCase()} ${appState.getNHICUserData.lastNameAr!.toUpperCase()}"); return Scaffold( From 3b5d2d27a46051ff433d25344360c41aabd92abb Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Thu, 18 Sep 2025 17:40:49 +0300 Subject: [PATCH 3/4] Updates --- lib/core/api_consts.dart | 2 +- lib/core/utils/utils.dart | 13 +- lib/extensions/string_extensions.dart | 15 +- .../book_appointments_view_model.dart | 3 +- .../appointment_via_region_viewmodel.dart | 3 +- .../my_appointments_view_model.dart | 5 + lib/features/payfort/payfort_view_model.dart | 4 +- .../appointment_details_page.dart | 13 +- .../appointment_payment_page.dart | 379 +++++++++--------- .../appointments/my_appointments_page.dart | 13 +- .../widgets/appointment_card.dart | 63 +-- .../appointment_checkin_bottom_sheet.dart | 19 +- .../authentication/saved_login_screen.dart | 4 +- .../book_appointment_page.dart | 39 +- .../review_appointment_page.dart | 3 +- .../search_doctor_by_name.dart | 3 +- .../book_appointment/select_clinic_page.dart | 5 +- .../book_appointment/select_doctor_page.dart | 3 +- .../select_livecare_clinic_page.dart | 3 +- .../widgets/appointment_calendar.dart | 13 +- .../habib_wallet/habib_wallet_page.dart | 3 +- lib/presentation/home/landing_page.dart | 206 ++++++---- .../home/widgets/habib_wallet_card.dart | 3 +- .../home/widgets/small_service_card.dart | 13 +- .../medical_file/medical_file_page.dart | 17 +- .../medical_file_appointment_card.dart | 5 +- .../widgets/patient_sick_leave_card.dart | 3 +- .../prescriptions_list_page.dart | 3 +- .../profile_settings/profile_settings.dart | 2 +- .../radiology/radiology_orders_page.dart | 270 ++++++------- .../radiology/radiology_result_page.dart | 76 ++++ lib/splashPage.dart | 3 +- 32 files changed, 674 insertions(+), 535 deletions(-) create mode 100644 lib/presentation/radiology/radiology_result_page.dart diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index f802818..d31679c 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -723,7 +723,7 @@ const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_In class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 152cd54..6b74a7b 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -346,9 +346,9 @@ class Utils { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Lottie.asset(AppAnimations.warningAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), + Lottie.asset(AppAnimations.warningAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 128.h, height: 128.h, fit: BoxFit.fill), SizedBox(height: 8.h), - (loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor), + (loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor, letterSpacing: 0), SizedBox(height: 16.h), isShowActionButtons ? Row( @@ -361,11 +361,11 @@ class Utils { onCancelTap(); } }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, icon: AppAssets.cancel, - iconColor: AppColors.primaryRedColor, + iconColor: AppColors.whiteColor, ), ), SizedBox(width: 8.h), @@ -652,6 +652,7 @@ class Utils { static Widget getPaymentAmountWithSymbol2(num habibWalletAmount, Color iconColor, double iconSize, {bool isSaudiCurrency = true, bool isExpanded = true}) { return RichText( + maxLines: 1, text: TextSpan( children: [ WidgetSpan( diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 7140c05..5465ce3 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -30,12 +30,11 @@ extension EmailValidator on String { fontStyle: fontStyle ?? FontStyle.normal, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, - letterSpacing: -1, + letterSpacing: 0, ), ); - Widget toText10({Color? color, FontWeight? weight, bool isBold = false, bool isUnderLine = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow, double letterSpacing = -1}) => - Text( + Widget toText10({Color? color, FontWeight? weight, bool isBold = false, bool isUnderLine = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow, double letterSpacing = 0}) => Text( this, maxLines: maxlines, overflow: textOverflow, @@ -49,7 +48,7 @@ extension EmailValidator on String { decorationColor: color ?? AppColors.blackColor), ); - Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = -1}) => Text( + Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0}) => Text( this, textAlign: isCenter ? TextAlign.center : null, maxLines: (maxLine > 0) ? maxLine : null, @@ -71,7 +70,7 @@ extension EmailValidator on String { fontSize: 12.fSize, fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, - letterSpacing: -1, + letterSpacing: 0, height: height, decorationColor: isUnderLine ? AppColors.blackColor : null, decoration: isUnderLine ? TextDecoration.underline : null, @@ -87,7 +86,7 @@ extension EmailValidator on String { fontSize: 12.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, - letterSpacing: -1, + letterSpacing: 0, decoration: isUnderLine ? TextDecoration.underline : null, ), ); @@ -120,7 +119,7 @@ extension EmailValidator on String { ), ); - Widget toText13({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0, FontWeight? weight, double? letterSpacing = -1}) => Text( + Widget toText13({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0, FontWeight? weight, double? letterSpacing = 0}) => Text( this, textAlign: isCenter ? TextAlign.center : null, maxLines: (maxLine > 0) ? maxLine : null, @@ -139,7 +138,7 @@ extension EmailValidator on String { bool isCenter = false, FontWeight? weight, int? maxlines, - double? letterSpacing = -1, + double? letterSpacing = 0, double? height, TextOverflow? textOverflow}) => Text( diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index c74b176..155a388 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -24,6 +24,7 @@ import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.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:location/location.dart' show Location; @@ -378,7 +379,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { LoadingUtils.hideFullScreenLoader(); Navigator.pushAndRemoveUntil( navigationService.navigatorKey.currentContext!, - FadePage( + CustomPageRoute( page: LandingNavigation(), ), (r) => false); diff --git a/lib/features/my_appointments/appointment_via_region_viewmodel.dart b/lib/features/my_appointments/appointment_via_region_viewmodel.dart index a49e1c4..1525280 100644 --- a/lib/features/my_appointments/appointment_via_region_viewmodel.dart +++ b/lib/features/my_appointments/appointment_via_region_viewmodel.dart @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart' show ChangeNotifier; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; enum AppointmentViaRegionState { @@ -39,7 +40,7 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier { void handleLastStep(){ navigationService.pop(); - navigationService.push(FadePage( + navigationService.push(CustomPageRoute( page: SelectClinicPage(), ),); } diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index 779f2b6..55fafc4 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -79,6 +79,11 @@ class MyAppointmentsViewModel extends ChangeNotifier { } Future getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async { + + patientAppointmentsHistoryList.clear(); + patientUpcomingAppointmentsHistoryList.clear(); + patientArrivedAppointmentsHistoryList.clear(); + final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: isActiveAppointment, isArrivedAppointments: isArrivedAppointments); final resultArrived = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true); diff --git a/lib/features/payfort/payfort_view_model.dart b/lib/features/payfort/payfort_view_model.dart index 9c9df6a..d99de80 100644 --- a/lib/features/payfort/payfort_view_model.dart +++ b/lib/features/payfort/payfort_view_model.dart @@ -99,6 +99,7 @@ class PayfortViewModel extends ChangeNotifier { String? applePayShaType, String? applePayShaRequestPhrase, }) async { + var sdkTokenResponse; try { String? deviceId = await _payfort.getDeviceId(); @@ -125,6 +126,7 @@ class PayfortViewModel extends ChangeNotifier { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { // payfortProjectDetailsRespModel = apiResponse.data!; + sdkTokenResponse = apiResponse.data; isApplePayConfigurationLoading = false; notifyListeners(); } @@ -133,7 +135,7 @@ class PayfortViewModel extends ChangeNotifier { } catch (e) { print("Error here: ${e.toString()}"); } - return null; + return sdkTokenResponse; } Future paymentWithApplePay({ diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 42ace37..e1c8662 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -28,6 +28,7 @@ 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'; 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/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:maps_launcher/maps_launcher.dart'; @@ -72,7 +73,7 @@ class _AppointmentDetailsPageState extends State { Expanded( child: CollapsingListView( title: "Appointment Details".needTranslation, - report: () {}, + report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? () {} : null, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -331,7 +332,7 @@ class _AppointmentDetailsPageState extends State { ).onPress(() { prescriptionVM.setPrescriptionsDetailsLoading(); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()), ), ); @@ -364,7 +365,7 @@ class _AppointmentDetailsPageState extends State { onPressed: () { Navigator.of(context) .push( - FadePage( + CustomPageRoute( page: PrescriptionsListPage(), ), ) @@ -425,8 +426,8 @@ class _AppointmentDetailsPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor), + Expanded(child: LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)), + "VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -2), ], ), SizedBox(height: 18.h), @@ -560,7 +561,7 @@ class _AppointmentDetailsPageState extends State { case 20: myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: AppointmentPaymentPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), ), ); diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index a617647..4d5fd8f 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -27,6 +27,7 @@ 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'; import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; @@ -79,118 +80,116 @@ class _AppointmentPaymentPageState extends State { child: CollapsingListView( title: "Appointment Payment".needTranslation, child: SingleChildScrollView( - child: myAppointmentsVM.isAppointmentPatientShareLoading - ? const MoviesShimmerWidget().paddingAll(24.h) - : Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 24.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + ), + child: Row( + mainAxisSize: MainAxisSize.max, children: [ - SizedBox(height: 24.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: false, - ), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset(AppAssets.mada, width: 72.h, height: 25.h), - SizedBox(height: 16.h), - "Mada".needTranslation.toText16(isBold: true), - ], - ), - SizedBox(width: 8.h), - const Spacer(), - Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, - iconColor: AppColors.blackColor, - width: 18.h, - height: 13.h, - fit: BoxFit.contain, - ), - ], - ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h).onPress(() { - selectedPaymentMethod = "MADA"; - openPaymentURL("mada"); - }), - SizedBox(height: 16.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: false, - ), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Image.asset(AppAssets.visa, width: 50.h, height: 50.h), - SizedBox(width: 8.h), - Image.asset(AppAssets.Mastercard, width: 40.h, height: 40.h), - ], - ), - SizedBox(height: 16.h), - "Visa or Mastercard".needTranslation.toText16(isBold: true), - ], - ), - SizedBox(width: 8.h), - const Spacer(), - Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, - iconColor: AppColors.blackColor, - width: 18.h, - height: 13.h, - fit: BoxFit.contain, - ), - ], - ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h).onPress(() { - selectedPaymentMethod = "VISA"; - openPaymentURL("visa"); - }), - SizedBox(height: 16.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: false, - ), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h), - SizedBox(height: 16.h), - "Tamara".needTranslation.toText16(isBold: true), - ], - ), - SizedBox(width: 8.h), - const Spacer(), - Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, - iconColor: AppColors.blackColor, - width: 18.h, - height: 13.h, - fit: BoxFit.contain, - ), - ], - ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h).onPress(() { - selectedPaymentMethod = "TAMARA"; - openPaymentURL("tamara"); - }), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset(AppAssets.mada, width: 72.h, height: 25.h).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), + SizedBox(height: 16.h), + "Mada".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), + ], + ), + SizedBox(width: 8.h), + const Spacer(), + Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, + iconColor: AppColors.blackColor, + width: 18.h, + height: 13.h, + fit: BoxFit.contain, + ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), + ], + ).paddingSymmetrical(16.h, 16.h), + ).paddingSymmetrical(24.h, 0.h).onPress(() { + selectedPaymentMethod = "MADA"; + openPaymentURL("mada"); + }), + SizedBox(height: 16.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Image.asset(AppAssets.visa, width: 50.h, height: 50.h), + SizedBox(width: 8.h), + Image.asset(AppAssets.Mastercard, width: 40.h, height: 40.h), + ], + ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), + SizedBox(height: 16.h), + "Visa or Mastercard".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), + ], + ), + SizedBox(width: 8.h), + const Spacer(), + Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, + iconColor: AppColors.blackColor, + width: 18.h, + height: 13.h, + fit: BoxFit.contain, + ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ], + ).paddingSymmetrical(16.h, 16.h), + ).paddingSymmetrical(24.h, 0.h).onPress(() { + selectedPaymentMethod = "VISA"; + openPaymentURL("visa"); + }), + SizedBox(height: 16.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, ), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), + SizedBox(height: 16.h), + "Tamara".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), + ], + ), + SizedBox(width: 8.h), + const Spacer(), + Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, + iconColor: AppColors.blackColor, + width: 18.h, + height: 13.h, + fit: BoxFit.contain, + ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), + ], + ).paddingSymmetrical(16.h, 16.h), + ).paddingSymmetrical(24.h, 0.h).onPress(() { + selectedPaymentMethod = "TAMARA"; + openPaymentURL("tamara"); + }), + ], + ), ), ), ), @@ -204,81 +203,79 @@ class _AppointmentPaymentPageState extends State { ), child: Consumer(builder: (context, payfortVM, child) { //TODO: Need to add loading state & animation for Apple Pay Configuration - return payfortVM.isApplePayConfigurationLoading - ? const MoviesShimmerWidget().paddingAll(16.h) - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (myAppointmentsVM.patientAppointmentShareResponseModel!.isCash ?? true) - ? Container( - height: 50.h, - decoration: ShapeDecoration( - color: AppColors.secondaryLightRedBorderColor, - shape: SmoothRectangleBorder( - borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), - smoothness: 1, - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "Insurance expired or inactive".needTranslation.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), - CustomButton( - text: LocaleKeys.updateInsurance.tr(context: context), - onPressed: () { - Navigator.of(context).push( - FadePage( - page: InsuranceHomePage(), - ), - ); - }, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.secondaryLightRedBorderColor, - textColor: AppColors.whiteColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(15, 0, 15, 0), - height: 30.h, - ).paddingSymmetrical(24.h, 0.h), - ], - ), - ) - : const SizedBox(), - SizedBox(height: 24.h), - "Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 17.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "Total amount to pay".needTranslation.toText14(isBold: true), - Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13, - isSaudiCurrency: true), - ], - ).paddingSymmetrical(24.h, 0.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor), - Utils.getPaymentAmountWithSymbol( - myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount!.toString().toText14(isBold: true, color: AppColors.greyTextColor), - AppColors.greyTextColor, - 13, - isSaudiCurrency: true), - ], - ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 17.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "".needTranslation.toText14(isBold: true), - Utils.getPaymentAmountWithSymbol( - myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax!.toString().toText24(isBold: true), AppColors.blackColor, 17, - isSaudiCurrency: true), - ], - ).paddingSymmetrical(24.h, 0.h), - //TODO: Add Apple Pay Privileges - Utils.buildSvgWithAssets( + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (myAppointmentsVM.patientAppointmentShareResponseModel!.isCash ?? true) + ? Container( + height: 50.h, + decoration: ShapeDecoration( + color: AppColors.secondaryLightRedBorderColor, + shape: SmoothRectangleBorder( + borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), + smoothness: 1, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Insurance expired or inactive".needTranslation.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), + CustomButton( + text: LocaleKeys.updateInsurance.tr(context: context), + onPressed: () { + Navigator.of(context).push( + CustomPageRoute( + page: InsuranceHomePage(), + ), + ); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.secondaryLightRedBorderColor, + textColor: AppColors.whiteColor, + fontSize: 10, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(15, 0, 15, 0), + height: 30.h, + ).paddingSymmetrical(24.h, 0.h), + ], + ), + ) + : const SizedBox(), + SizedBox(height: 24.h), + "Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 17.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Total amount to pay".needTranslation.toText14(isBold: true), + Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13, + isSaudiCurrency: true), + ], + ).paddingSymmetrical(24.h, 0.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor), + Utils.getPaymentAmountWithSymbol( + myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount!.toString().toText14(isBold: true, color: AppColors.greyTextColor), + AppColors.greyTextColor, + 13, + isSaudiCurrency: true), + ], + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 17.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "".needTranslation.toText14(isBold: true), + Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax!.toString().toText24(isBold: true), AppColors.blackColor, 17, + isSaudiCurrency: true), + ], + ).paddingSymmetrical(24.h, 0.h), + //TODO: Add Apple Pay Privileges + Platform.isIOS + ? Utils.buildSvgWithAssets( icon: AppAssets.apple_pay_button, width: 200.h, height: 80.h, @@ -286,10 +283,11 @@ class _AppointmentPaymentPageState extends State { ).paddingSymmetrical(24.h, 0.h).onPress(() { // payfortVM.setIsApplePayConfigurationLoading(true); startApplePay(); - }), - SizedBox(height: 12.h), - ], - ); + }) + : SizedBox(height: 12.h), + SizedBox(height: 12.h), + ], + ); }), ), ], @@ -389,12 +387,12 @@ class _AppointmentPaymentPageState extends State { Navigator.of(context).pop(); Navigator.pushAndRemoveUntil( context, - FadePage( + CustomPageRoute( page: LandingNavigation(), ), (r) => false); Navigator.of(context).push( - FadePage(page: MyAppointmentsPage()), + CustomPageRoute(page: MyAppointmentsPage()), ); }); }); @@ -528,6 +526,7 @@ class _AppointmentPaymentPageState extends State { onSucceeded: (successResult) async { Navigator.of(context).pop(); log("successResult: ${successResult.responseMessage.toString()}"); + selectedPaymentMethod = successResult.paymentOption ?? "VISA"; checkPaymentStatus(); }, // projectId: appo.projectID, diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart index 4c16a17..5972435 100644 --- a/lib/presentation/appointments/my_appointments_page.dart +++ b/lib/presentation/appointments/my_appointments_page.dart @@ -6,6 +6,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/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart'; import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; @@ -85,7 +86,15 @@ class _MyAppointmentsPageState extends State { : 1, itemBuilder: (context, index) { return myAppointmentsVM.isMyAppointmentsLoading - ? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.h) + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + child: AppointmentCard( + patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), + myAppointmentsViewModel: myAppointmentsViewModel, + isLoading: true, + isFromHomePage: false, + ), + ).paddingSymmetrical(24.h, 0.h) : myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty ? AnimationConfiguration.staggeredList( position: index, @@ -100,6 +109,8 @@ class _MyAppointmentsPageState extends State { child: AppointmentCard( patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index], myAppointmentsViewModel: myAppointmentsViewModel, + isLoading: false, + isFromHomePage: false, ), ).paddingSymmetrical(24.h, 0.h), ), diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 29694ed..a87012c 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -16,15 +16,16 @@ import 'package:hmg_patient_app_new/presentation/appointments/appointment_detail 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/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; -import 'package:smooth_corner/smooth_corner.dart'; class AppointmentCard extends StatefulWidget { - AppointmentCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel, this.isLoading = false}); + AppointmentCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel, this.isLoading = false, this.isFromHomePage = false}); PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; MyAppointmentsViewModel myAppointmentsViewModel; bool isLoading; + bool isFromHomePage; @override State createState() => _AppointmentCardState(); @@ -38,7 +39,7 @@ class _AppointmentCardState extends State { onTap: () { Navigator.of(context) .push( - FadePage( + CustomPageRoute( page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), ), ) @@ -66,9 +67,11 @@ class _AppointmentCardState extends State { mainAxisSize: MainAxisSize.min, children: [ CustomButton( - text: appState.isArabic() - ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! - : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!, + text: widget.isLoading + ? "OutPatient" + : appState.isArabic() + ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! + : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!, onPressed: () {}, backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), borderColor: AppColors.primaryRedColor.withOpacity(0.0), @@ -85,7 +88,7 @@ class _AppointmentCardState extends State { mainAxisSize: MainAxisSize.min, children: [ CustomButton( - text: AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!), + text: widget.isLoading ? "Booked" : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!), onPressed: () {}, backgroundColor: AppColors.successColor.withOpacity(0.1), borderColor: AppColors.successColor.withOpacity(0.0), @@ -99,11 +102,11 @@ class _AppointmentCardState extends State { ], ), ], - ), + ).toShimmer2(isShow: widget.isLoading), ), // TODO: Implement the logic to enable/disable the switch based on reminder status AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) - ? SizedBox() + ? SizedBox().toShimmer2(isShow: widget.isLoading) : Switch( activeColor: AppColors.successColor, activeTrackColor: AppColors.successColor.withValues(alpha: .15), @@ -115,13 +118,13 @@ class _AppointmentCardState extends State { return const Icon(Icons.close); // Icon when switch is OFF }, ), - value: widget.patientAppointmentHistoryResponseModel.hasReminder!, + value: widget.isLoading ? false : widget.patientAppointmentHistoryResponseModel.hasReminder!, onChanged: (newValue) { setState(() { widget.myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel); }); }, - ), + ).toShimmer2(isShow: widget.isLoading), ], ), SizedBox(height: 16.h), @@ -129,30 +132,38 @@ class _AppointmentCardState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Image.network( - widget.patientAppointmentHistoryResponseModel.doctorImageURL!, + widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorImageURL!, width: 63.h, height: 63.h, fit: BoxFit.fill, - ).circle(100), + ).circle(100).toShimmer2(isShow: widget.isLoading), SizedBox(width: 16.h), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - widget.patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(isBold: true), + (widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorNameObj!) + .toText16(isBold: true) + .toShimmer2(isShow: widget.isLoading), + SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 3.h, runSpacing: 4.h, children: [ - AppCustomChipWidget(labelText: widget.patientAppointmentHistoryResponseModel.clinicName!), - AppCustomChipWidget(labelText: widget.patientAppointmentHistoryResponseModel.projectName!), + widget.isFromHomePage ? SizedBox.shrink() : AppCustomChipWidget(labelText: widget.isLoading ? "Cardiology" : widget.patientAppointmentHistoryResponseModel.clinicName!).toShimmer2(isShow: widget.isLoading), + widget.isFromHomePage ? SizedBox.shrink() : AppCustomChipWidget(labelText: widget.isLoading ? "Olaya" : widget.patientAppointmentHistoryResponseModel.projectName!).toShimmer2(isShow: widget.isLoading), AppCustomChipWidget( - icon: AppAssets.appointment_calendar_icon, - labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)), + icon: AppAssets.appointment_calendar_icon, + labelText: + widget.isLoading ? "Cardiology" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)) + .toShimmer2(isShow: widget.isLoading), AppCustomChipWidget( - icon: AppAssets.appointment_time_icon, - labelText: DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)), + icon: AppAssets.appointment_time_icon, + labelText: widget.isLoading + ? "Cardiology" + : DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)) + .toShimmer2(isShow: widget.isLoading), ], ), ], @@ -166,12 +177,12 @@ class _AppointmentCardState extends State { Expanded( flex: 6, child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) - ? getArrivedAppointmentButton() + ? getArrivedAppointmentButton().toShimmer2(isShow: widget.isLoading) : CustomButton( text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), onPressed: () { Navigator.of(context) - .push(FadePage( + .push(CustomPageRoute( page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), )) .then((val) { @@ -189,8 +200,8 @@ class _AppointmentCardState extends State { height: 40.h, icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction), iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), - iconSize: 14.h, - ), + iconSize: 15.h, + ).toShimmer2(isShow: widget.isLoading), ), SizedBox(width: 8.h), Expanded( @@ -211,10 +222,10 @@ class _AppointmentCardState extends State { fit: BoxFit.contain, ), ), - ).onPress(() { + ).toShimmer2(isShow: widget.isLoading).onPress(() { Navigator.of(context) .push( - FadePage( + CustomPageRoute( page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), ), ) diff --git a/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart b/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart index 77fec61..1c9649b 100644 --- a/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart +++ b/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart @@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/nfc/nfc_reader_sheet.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 AppointmentCheckinBottomSheet extends StatelessWidget { @@ -110,12 +111,14 @@ class AppointmentCheckinBottomSheet extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - title.toText16(isBold: true, color: AppColors.textColor), - subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - ], + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title.toText16(isBold: true, color: AppColors.textColor), + subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + ], + ), ), Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon, @@ -144,12 +147,12 @@ class AppointmentCheckinBottomSheet extends StatelessWidget { Navigator.of(context).pop(); Navigator.pushAndRemoveUntil( context, - FadePage( + CustomPageRoute( page: LandingNavigation(), ), (r) => false); Navigator.of(context).push( - FadePage(page: MyAppointmentsPage()), + CustomPageRoute(page: MyAppointmentsPage()), ); }, isFullScreen: false); }, diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart index 073ff8b..44918ed 100644 --- a/lib/presentation/authentication/saved_login_screen.dart +++ b/lib/presentation/authentication/saved_login_screen.dart @@ -65,7 +65,7 @@ class _SavedLogin extends State { body: SafeArea( child: Padding( padding: EdgeInsets.symmetric(horizontal: 24.h), - child: Column( + child: appState.getSelectDeviceByImeiRespModelElement != null ? Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ const Spacer(flex: 2), @@ -297,7 +297,7 @@ class _SavedLogin extends State { ), const SizedBox(height: 20), ], - ), + ) : SizedBox.shrink(), ), ), ); diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index 482d864..825a9e9 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -14,15 +14,14 @@ import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_reg import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart'; -import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' - show RegionBottomSheetBody; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody; import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart'; import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; -import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart' - show showCommonBottomSheetWithoutHeight; +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/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; @@ -52,13 +51,12 @@ class _BookAppointmentPageState extends State { Widget build(BuildContext context) { bookAppointmentsViewModel = Provider.of(context, listen: false); appState = getIt.get(); - regionalViewModel = - Provider.of(context, listen: true); + regionalViewModel = Provider.of(context, listen: true); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: LocaleKeys.bookAppo.tr(context: context), - isLeading: false, + isLeading: Navigator.canPop(context), child: SingleChildScrollView( child: Consumer(builder: (context, bookAppointmentsVM, child) { return Column( @@ -125,7 +123,7 @@ class _BookAppointmentPageState extends State { bookAppointmentsViewModel.setLoadSpecificClinic(false); bookAppointmentsViewModel.setProjectID(null); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: SelectClinicPage(), ), ); @@ -154,7 +152,7 @@ class _BookAppointmentPageState extends State { ).onPress(() { bookAppointmentsViewModel.setIsDoctorSearchByNameStarted(false); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: SearchDoctorByName(), ), ); @@ -198,24 +196,21 @@ class _BookAppointmentPageState extends State { void openRegionListBottomSheet(BuildContext context) { regionalViewModel.flush(); // AppointmentViaRegionViewmodel? viewmodel = null; - showCommonBottomSheetWithoutHeight(context, - title: "", - titleWidget: Consumer( - builder: (_, data, __) => getTitle(data)), - isDismissible: false, + showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer(builder: (_, data, __) => getTitle(data)), isDismissible: false, child: Consumer(builder: (_, data, __) { return getRegionalSelectionWidget(data); - }), callBackFunc: () { - }); + }), callBackFunc: () {}); } Widget getRegionalSelectionWidget(AppointmentViaRegionViewmodel data) { if (data.bottomSheetState == AppointmentViaRegionState.REGION_SELECTION) { return RegionBottomSheetBody(); } - if(data.bottomSheetState == AppointmentViaRegionState.TYPE_SELECTION){ + if (data.bottomSheetState == AppointmentViaRegionState.TYPE_SELECTION) { bookAppointmentsViewModel.resetFilterList(); - return FacilityTypeSelectionWidget(selectedRegion: data.selectedRegionId??"",); + return FacilityTypeSelectionWidget( + selectedRegion: data.selectedRegionId ?? "", + ); } if (data.bottomSheetState == AppointmentViaRegionState.HOSPITAL_SELECTION) { return HospitalBottomSheetBody(); @@ -225,9 +220,7 @@ class _BookAppointmentPageState extends State { bookAppointmentsViewModel.setIsClinicsListLoading(true); bookAppointmentsViewModel.setLoadSpecificClinic(true); bookAppointmentsViewModel.setProjectID(regionalViewModel.selectedHospital?.hospitalList.first?.mainProjectID.toString()); - - } - else { + } else { SizedBox.shrink(); } return SizedBox.shrink(); @@ -237,9 +230,7 @@ class _BookAppointmentPageState extends State { if (data.selectedRegionId == null) { return LocaleKeys.selectRegion.tr().toText20(weight: FontWeight.w600); } else { - return Utils.buildSvgWithAssets( - icon: AppAssets.arrow_back, iconColor: Color(0xff2B353E)) - .onPress(() { + return Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, iconColor: Color(0xff2B353E)).onPress(() { data.handleBackPress(); }); } diff --git a/lib/presentation/book_appointment/review_appointment_page.dart b/lib/presentation/book_appointment/review_appointment_page.dart index bd43a9f..d3804d8 100644 --- a/lib/presentation/book_appointment/review_appointment_page.dart +++ b/lib/presentation/book_appointment/review_appointment_page.dart @@ -16,6 +16,7 @@ import 'package:hmg_patient_app_new/presentation/lab/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/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; @@ -214,7 +215,7 @@ class _ReviewAppointmentPageState extends State { LoadingUtils.hideFullScreenLoader(); Navigator.pushAndRemoveUntil( context, - FadePage( + CustomPageRoute( page: LandingNavigation(), ), (r) => false); diff --git a/lib/presentation/book_appointment/search_doctor_by_name.dart b/lib/presentation/book_appointment/search_doctor_by_name.dart index 23c5c18..e6b0ba1 100644 --- a/lib/presentation/book_appointment/search_doctor_by_name.dart +++ b/lib/presentation/book_appointment/search_doctor_by_name.dart @@ -18,6 +18,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.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'; @@ -122,7 +123,7 @@ class _SearchDoctorByNameState extends State { await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) { LoaderBottomSheet.hideLoader(); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: DoctorProfilePage(), ), ); diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index 214d4ad..0580480 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/clinic import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/input_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'; @@ -141,13 +142,13 @@ class _SelectClinicPageState extends State { bookAppointmentsViewModel.setIsDoctorsListLoading(true); if (clinic.isLiveCareClinicAndOnline ?? false) { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: SelectLivecareClinicPage(), ), ); } else { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: SelectDoctorPage(), ), ); diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart index 43449e4..27a60f6 100644 --- a/lib/presentation/book_appointment/select_doctor_page.dart +++ b/lib/presentation/book_appointment/select_doctor_page.dart @@ -20,6 +20,7 @@ import 'package:hmg_patient_app_new/theme/colors.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'; @@ -124,7 +125,7 @@ class _SelectDoctorPageState extends State { await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) { LoaderBottomSheet.hideLoader(); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: DoctorProfilePage(), ), ); diff --git a/lib/presentation/book_appointment/select_livecare_clinic_page.dart b/lib/presentation/book_appointment/select_livecare_clinic_page.dart index ef682c3..76d85af 100644 --- a/lib/presentation/book_appointment/select_livecare_clinic_page.dart +++ b/lib/presentation/book_appointment/select_livecare_clinic_page.dart @@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_ import 'package:hmg_patient_app_new/presentation/lab/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 { @@ -122,7 +123,7 @@ class SelectLivecareClinicPage extends StatelessWidget { onPressed: () { Navigator.of(context).pop(); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: SelectDoctorPage(), ), ); diff --git a/lib/presentation/book_appointment/widgets/appointment_calendar.dart b/lib/presentation/book_appointment/widgets/appointment_calendar.dart index c880238..046003a 100644 --- a/lib/presentation/book_appointment/widgets/appointment_calendar.dart +++ b/lib/presentation/book_appointment/widgets/appointment_calendar.dart @@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/presentation/home/navigation_screen.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'; +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:lottie/lottie.dart'; import 'package:provider/provider.dart'; @@ -106,7 +107,7 @@ class _AppointmentCalendarState extends State { ), view: CalendarView.month, todayHighlightColor: Colors.transparent, - todayTextStyle: TextStyle(color: AppColors.textColor), + todayTextStyle: TextStyle(color: AppColors.textColor, fontWeight: FontWeight.bold), selectionDecoration: ShapeDecoration( color: AppColors.transparent, shape: SmoothRectangleBorder( @@ -151,8 +152,8 @@ class _AppointmentCalendarState extends State { child: Wrap( direction: Axis.horizontal, alignment: WrapAlignment.start, - spacing: 8.h, - runSpacing: 8.h, + spacing: 6.h, + runSpacing: 6.h, children: List.generate( dayEvents.length, // Generate a large number of items to ensure scrolling (index) => TimeSlotChip( @@ -177,7 +178,7 @@ class _AppointmentCalendarState extends State { bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime); Navigator.of(context).pop(); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: ReviewAppointmentPage(), ), ); @@ -216,7 +217,7 @@ class _AppointmentCalendarState extends State { Navigator.of(context).pop(); Navigator.pushAndRemoveUntil( context, - FadePage( + CustomPageRoute( page: LandingNavigation(), ), (r) => false); @@ -320,7 +321,7 @@ class TimeSlotChip extends StatelessWidget { return GestureDetector( onTap: onTap, child: Container( - padding: EdgeInsets.symmetric(horizontal: 18.h, vertical: 8.h), + padding: EdgeInsets.symmetric(horizontal: 14.h, vertical: 8.h), decoration: ShapeDecoration( color: AppColors.whiteColor, shape: SmoothRectangleBorder( diff --git a/lib/presentation/habib_wallet/habib_wallet_page.dart b/lib/presentation/habib_wallet/habib_wallet_page.dart index 1be3e12..6add7bd 100644 --- a/lib/presentation/habib_wallet/habib_wallet_page.dart +++ b/lib/presentation/habib_wallet/habib_wallet_page.dart @@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_pa import 'package:hmg_patient_app_new/presentation/lab/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'; import 'package:provider/provider.dart'; @@ -84,7 +85,7 @@ class _HabibWalletState extends State { text: "Recharge".needTranslation, onPressed: () { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: RechargeWalletPage(), ), ); diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 01b1490..ff17d63 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -16,10 +16,12 @@ 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/habib_wallet/habib_wallet_view_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart'; import 'package:hmg_patient_app_new/presentation/authentication/quick_login.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart'; @@ -98,39 +100,37 @@ class _LandingPageState extends State { children: [ Row( spacing: 8.h, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - appState.isAuthenticated - ? WelcomeWidget( - onTap: () { - Navigator.of(context).push( - springPageRoute(ProfileSettings()) - ); - }, - name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), - imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, - ).expanded - : CustomButton( - text: LocaleKeys.loginOrRegister.tr(context: context), - onPressed: () async { - await authVM.onLoginPressed(); - }, - backgroundColor: Color(0xffFEE9EA), - borderColor: Color(0xffFEE9EA), - textColor: Color(0xffED1C2B), - fontSize: 16, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 50, - ), - Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + appState.isAuthenticated + ? WelcomeWidget( + onTap: () { + Navigator.of(context).push(springPageRoute(ProfileSettings())); + }, + name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), + imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, + ).expanded + : CustomButton( + text: LocaleKeys.loginOrRegister.tr(context: context), + onPressed: () async { + await authVM.onLoginPressed(); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 16, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 50, + ), + Row( mainAxisSize: MainAxisSize.min, spacing: 12.h, children: [ Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 20, width: 20).onPress(() { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: MedicalFilePage(), // page: LoginScreen(), ), @@ -138,7 +138,7 @@ class _LandingPageState extends State { }), Utils.buildSvgWithAssets(icon: AppAssets.search_icon, height: 20, width: 20).onPress(() { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: MedicalFilePage(), // page: LoginScreen(), ), @@ -146,7 +146,7 @@ class _LandingPageState extends State { }), Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 20, width: 20).onPress(() { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: MedicalFilePage(), // page: LoginScreen(), ), @@ -180,62 +180,94 @@ class _LandingPageState extends State { ); }), SizedBox(height: 12.h), - Swiper( - itemCount: 3, - layout: SwiperLayout.STACK, - loop: true, - itemWidth: MediaQuery.of(context).size.width - 42, - indicatorLayout: PageIndicatorLayout.COLOR, - axisDirection: AxisDirection.right, - controller: _controller, - itemHeight: 210 + 16, - pagination: const SwiperPagination( - alignment: Alignment.bottomCenter, - margin: EdgeInsets.only(top: 210 + 8 + 24), - builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), - ), - itemBuilder: (BuildContext context, int index) { - return FamilyCardWidget().paddingOnly(right: 16); - }, - ), - // Container( - // width: double.infinity, - // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - // color: AppColors.whiteColor, - // borderRadius: 24, - // ), - // child: Padding( - // padding: EdgeInsets.all(12.h), - // child: Column( - // children: [ - // Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), - // SizedBox(height: 12.h), - // "You do not have any upcoming appointment. Please book an appointment".toText12(isCenter: true), - // SizedBox(height: 12.h), - // CustomButton( - // text: LocaleKeys.bookAppo.tr(context: context), - // onPressed: () { - // Navigator.of(context).push( - // FadePage( - // page: BookAppointmentPage(), - // ), - // ); - // }, - // backgroundColor: Color(0xffFEE9EA), - // borderColor: Color(0xffFEE9EA), - // textColor: Color(0xffED1C2B), - // fontSize: 14, - // fontWeight: FontWeight.w500, - // borderRadius: 12, - // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - // height: 40, - // icon: AppAssets.add_icon, - // iconColor: AppColors.primaryRedColor, - // ), - // ], - // ), - // ), - // ).paddingSymmetrical(24.h, 0.h), + Consumer(builder: (context, myAppointmentsVM, child) { + return myAppointmentsVM.isMyAppointmentsLoading + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + child: AppointmentCard( + patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), + myAppointmentsViewModel: myAppointmentsViewModel, + isLoading: true, + isFromHomePage: true, + ), + ).paddingSymmetrical(24.h, 0.h) + : myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty + ? myAppointmentsVM.patientAppointmentsHistoryList.length == 1 + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + child: AppointmentCard( + patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first, + myAppointmentsViewModel: myAppointmentsViewModel, + isLoading: false, + isFromHomePage: true, + ), + ).paddingSymmetrical(24.h, 0.h) + : Swiper( + itemCount: myAppointmentsVM.isMyAppointmentsLoading + ? 3 + : myAppointmentsVM.patientAppointmentsHistoryList.length < 3 + ? myAppointmentsVM.patientAppointmentsHistoryList.length + : 3, + layout: SwiperLayout.STACK, + loop: true, + itemWidth: MediaQuery.of(context).size.width - 72, + indicatorLayout: PageIndicatorLayout.COLOR, + axisDirection: AxisDirection.right, + controller: _controller, + itemHeight: 210 + 25, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + margin: EdgeInsets.only(top: 210 + 8 + 24), + builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), + ), + itemBuilder: (BuildContext context, int index) { + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + child: AppointmentCard( + patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index], + myAppointmentsViewModel: myAppointmentsViewModel, + isLoading: false, + isFromHomePage: true, + ), + ); + }, + ) + : Container( + width: double.infinity, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24, hasShadow: true), + child: Padding( + padding: EdgeInsets.all(12.h), + child: Column( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), + SizedBox(height: 12.h), + "You do not have any upcoming appointment. Please book an appointment".needTranslation.toText12(isCenter: true), + SizedBox(height: 12.h), + CustomButton( + text: LocaleKeys.bookAppo.tr(context: context), + onPressed: () { + Navigator.of(context).push( + CustomPageRoute( + page: BookAppointmentPage(), + ), + ); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40, + icon: AppAssets.add_icon, + iconColor: AppColors.primaryRedColor, + ), + ], + ), + ), + ).paddingSymmetrical(24.h, 0.h); + }), SizedBox(height: 12.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -251,7 +283,7 @@ class _LandingPageState extends State { ], ).paddingSymmetrical(24.h, 0.h).onPress(() { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: MedicalFilePage(), ), ); diff --git a/lib/presentation/home/widgets/habib_wallet_card.dart b/lib/presentation/home/widgets/habib_wallet_card.dart index 3509000..6ea4507 100644 --- a/lib/presentation/home/widgets/habib_wallet_card.dart +++ b/lib/presentation/home/widgets/habib_wallet_card.dart @@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_mode import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; @@ -98,7 +99,7 @@ class HabibWalletCard extends StatelessWidget { ], ).onPress(() { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: HabibWalletPage(), ), ); diff --git a/lib/presentation/home/widgets/small_service_card.dart b/lib/presentation/home/widgets/small_service_card.dart index 6e1c588..fba96eb 100644 --- a/lib/presentation/home/widgets/small_service_card.dart +++ b/lib/presentation/home/widgets/small_service_card.dart @@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.d import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.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 '../../../core/utils/utils.dart'; @@ -61,28 +62,28 @@ class SmallServiceCard extends StatelessWidget { switch (serviceName) { case "lab_results": Navigator.of(context).push( - FadePage( + CustomPageRoute( page: LabOrdersPage(), ), ); break; case "radiology_results": Navigator.of(context).push( - FadePage( + CustomPageRoute( page: RadiologyOrdersPage(), ), ); break; case "prescriptions": Navigator.of(context).push( - FadePage( + CustomPageRoute( page: PrescriptionsListPage(), ), ); break; case "insurance_update": Navigator.of(context).push( - FadePage( + CustomPageRoute( page: InsuranceHomePage(), ), ); @@ -90,7 +91,7 @@ class SmallServiceCard extends StatelessWidget { case "my_doctors": Navigator.of(context).push( - FadePage( + CustomPageRoute( page: MyDoctorsPage(), ), ); @@ -98,7 +99,7 @@ class SmallServiceCard extends StatelessWidget { case "sick_leaves": Navigator.of(context).push( - FadePage( + CustomPageRoute( page: PatientSickleavesListPage(), ), ); diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index acaf622..09ab37e 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -40,6 +40,7 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.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/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; @@ -314,7 +315,7 @@ class _MedicalFilePageState extends State { ], ).paddingSymmetrical(24.h, 0.h).onPress(() { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: MyAppointmentsPage(), ), ); @@ -447,7 +448,7 @@ class _MedicalFilePageState extends State { ).onPress(() { prescriptionVM.setPrescriptionsDetailsLoading(); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: PrescriptionDetailPage(prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]), ), ); @@ -468,7 +469,7 @@ class _MedicalFilePageState extends State { text: "All Prescriptions".needTranslation, onPressed: () { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: PrescriptionsListPage(), ), ); @@ -526,7 +527,7 @@ class _MedicalFilePageState extends State { myAppointmentsViewModel.setIsPatientMyDoctorsLoading(true); myAppointmentsViewModel.getPatientMyDoctors(); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: MyDoctorsPage(), ), ); @@ -631,7 +632,7 @@ class _MedicalFilePageState extends State { iconSize: 40.h, ).onPress(() { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: VaccineListPage(), ), ); @@ -673,7 +674,7 @@ class _MedicalFilePageState extends State { iconSize: 36.h) .onPress(() { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: InsuranceHomePage(), ), ); @@ -747,7 +748,7 @@ class _MedicalFilePageState extends State { medicalFileViewModel.setIsPatientMedicalReportsLoading(true); medicalFileViewModel.getPatientMedicalReportList(); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: MedicalReportsPage(), ), ); @@ -761,7 +762,7 @@ class _MedicalFilePageState extends State { iconSize: 40.h, ).onPress(() { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: PatientSickleavesListPage(), ), ); diff --git a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart index 6d13ee7..a991ab8 100644 --- a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart +++ b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart @@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/appointment_details_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; class MedicalFileAppointmentCard extends StatelessWidget { @@ -86,7 +87,7 @@ class MedicalFileAppointmentCard extends StatelessWidget { text: AppointmentType.getNextActionText(patientAppointmentHistoryResponseModel.nextAction), onPressed: () { Navigator.of(context) - .push(FadePage( + .push(CustomPageRoute( page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel), )) .then((val) { @@ -129,7 +130,7 @@ class MedicalFileAppointmentCard extends StatelessWidget { ).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading).onPress(() { Navigator.of(context) .push( - FadePage( + CustomPageRoute( page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel), ), ) 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 438013f..e1999a7 100644 --- a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart +++ b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart @@ -17,6 +17,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:open_filex/open_filex.dart'; import 'package:provider/provider.dart'; @@ -147,7 +148,7 @@ class PatientSickLeaveCard extends StatelessWidget { ), ).toShimmer2(isShow: isLoading).onPress(() { Navigator.of(context).push( - FadePage( + CustomPageRoute( page: PatientSickleavesListPage(), ), ); diff --git a/lib/presentation/prescriptions/prescriptions_list_page.dart b/lib/presentation/prescriptions/prescriptions_list_page.dart index 148afb1..3d246f3 100644 --- a/lib/presentation/prescriptions/prescriptions_list_page.dart +++ b/lib/presentation/prescriptions/prescriptions_list_page.dart @@ -15,6 +15,7 @@ import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; @@ -258,7 +259,7 @@ class _PrescriptionsListPageState extends State { ).onPress(() { model.setPrescriptionsDetailsLoading(); Navigator.of(context).push( - FadePage( + CustomPageRoute( page: PrescriptionDetailPage(prescriptionsResponseModel: prescription), ), ); diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart index 832031a..4610361 100644 --- a/lib/presentation/profile_settings/profile_settings.dart +++ b/lib/presentation/profile_settings/profile_settings.dart @@ -115,7 +115,7 @@ class _ProfileSettingsState extends State { iconSize: 24.h, iconColor: AppColors.infoColor, textColor: AppColors.infoColor, - text: "Recharge", + text: "Recharge".needTranslation, borderWidth: 0.h, fontWeight: FontWeight.w500, borderColor: Colors.transparent, diff --git a/lib/presentation/radiology/radiology_orders_page.dart b/lib/presentation/radiology/radiology_orders_page.dart index 19d0908..ad28a5a 100644 --- a/lib/presentation/radiology/radiology_orders_page.dart +++ b/lib/presentation/radiology/radiology_orders_page.dart @@ -12,8 +12,11 @@ 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/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/presentation/radiology/radiology_result_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:provider/provider.dart'; @@ -49,156 +52,145 @@ class _RadiologyOrdersPageState extends State { child: SingleChildScrollView( child: Consumer( builder: (context, model, child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 16.h), - // Expandable list - ListView.builder( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: model.isRadiologyOrdersLoading ? 5 : model.patientRadiologyOrders.length, - itemBuilder: (context, index) { - final isExpanded = expandedIndex == index; - return model.isRadiologyOrdersLoading - ? const MoviesShimmerWidget() - : 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, - margin: EdgeInsets.symmetric(vertical: 8.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), - child: InkWell( - onTap: () { - setState(() { - expandedIndex = isExpanded ? null : index; - }); - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + return Padding( + padding: EdgeInsets.symmetric(horizontal: 24.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Expandable list + ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: model.isRadiologyOrdersLoading ? 5 : model.patientRadiologyOrders.length, + itemBuilder: (context, index) { + final isExpanded = expandedIndex == index; + return 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, + margin: EdgeInsets.symmetric(vertical: 8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), + child: InkWell( + onTap: () { + setState(() { + expandedIndex = isExpanded ? null : index; + }); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AppCustomChipWidget( + labelText: LocaleKeys.resultsAvailable.tr(context: context), + backgroundColor: AppColors.successColor.withOpacity(0.15), + textColor: AppColors.successColor, + ).toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 100), + SizedBox(height: 8.h), + Row( children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - CustomButton( - text: LocaleKeys.resultsAvailable.tr(context: context), - onPressed: () {}, - backgroundColor: AppColors.successColor.withOpacity(0.15), - borderColor: AppColors.successColor.withOpacity(0.01), - textColor: AppColors.successColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ), - Icon(isExpanded ? Icons.expand_less : Icons.expand_more), - ], - ), - SizedBox(height: 8.h), - Row( - children: [ - Image.network( - model.patientRadiologyOrders[index].doctorImageURL!, - width: 24.h, - height: 24.h, - fit: BoxFit.fill, - ).circle(100), - SizedBox(width: 4.h), - model.patientRadiologyOrders[index].doctorName!.toText16(isBold: true) - ], - ), - SizedBox(height: 8.h), - Row( + Image.network( + model.isRadiologyOrdersLoading + ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" + : model.patientRadiologyOrders[index].doctorImageURL!, + width: 24.h, + height: 24.h, + fit: BoxFit.fill, + ).circle(100).toShimmer2(isShow: model.isRadiologyOrdersLoading), + SizedBox(width: 4.h), + (model.isRadiologyOrdersLoading ? "Dr John Smith" : model.patientRadiologyOrders[index].doctorName!) + .toText16(isBold: true) + .toShimmer2(isShow: model.isRadiologyOrdersLoading) + ], + ), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget( + icon: AppAssets.doctor_calendar_icon, + labelText: model.isRadiologyOrdersLoading + ? "01 Jan 2025" + : DateUtil.formatDateToDate(DateUtil.convertStringToDate(model.patientRadiologyOrders[index].orderDate), false), + ).toShimmer2(isShow: model.isRadiologyOrdersLoading), + AppCustomChipWidget( + labelText: model.isRadiologyOrdersLoading ? "01 Jan 2025" : model.patientRadiologyOrders[index].clinicDescription!, + ).toShimmer2(isShow: model.isRadiologyOrdersLoading), + + // AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h), + // AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h), + ], + ), + ], + ), + ), + model.isRadiologyOrdersLoading + ? SizedBox.shrink() + : AnimatedCrossFade( + firstChild: SizedBox.shrink(), + secondChild: Padding( + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - CustomButton( - text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(model.patientRadiologyOrders[index].orderDate), false), - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 12, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 24.h, + Padding( + padding: EdgeInsets.only(bottom: 8.h), + child: '● ${model.patientRadiologyOrders[index].description}'.toText14(weight: FontWeight.w500), ), - SizedBox(width: 8.h), - CustomButton( - text: model.patientRadiologyOrders[index].clinicDescription!, - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 12, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 24.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: () { + Navigator.of(context).push( + CustomPageRoute( + page: RadiologyResultPage(patientRadiologyResponseModel: model.patientRadiologyOrders[index]), + ), + ); + }, + 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, + ), + ], ), ], ), - ], - ), - ), - AnimatedCrossFade( - firstChild: SizedBox.shrink(), - secondChild: Padding( - padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only(bottom: 8.h), - child: '● ${model.patientRadiologyOrders[index].description}'.toText14(weight: FontWeight.w500), - ), - 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, - ), - ], - ), - ], ), + crossFadeState: isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, + duration: Duration(milliseconds: 300), ), - crossFadeState: isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, - duration: Duration(milliseconds: 300), - ), - ], - ), - ), + ], ), ), ), - ); - }, - ), - ], + ), + ), + ); + }, + ), + ], + ), ); }, ), diff --git a/lib/presentation/radiology/radiology_result_page.dart b/lib/presentation/radiology/radiology_result_page.dart new file mode 100644 index 0000000..cc4a4e7 --- /dev/null +++ b/lib/presentation/radiology/radiology_result_page.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.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/extensions/widget_extensions.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/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; + +class RadiologyResultPage extends StatefulWidget { + RadiologyResultPage({super.key, required this.patientRadiologyResponseModel}); + + PatientRadiologyResponseModel patientRadiologyResponseModel; + + @override + State createState() => _RadiologyResultPageState(); +} + +class _RadiologyResultPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: CollapsingListView( + title: "Radiology Result".needTranslation, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 24.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 16.h), + widget.patientRadiologyResponseModel.description!.toText16(isBold: true), + SizedBox(height: 8.h), + widget.patientRadiologyResponseModel.reportData!.trim().toText12(isBold: true, color: AppColors.textColorLight), + SizedBox(height: 16.h), + CustomButton( + text: "View Radiology Image".needTranslation, + onPressed: () async {}, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + icon: AppAssets.calendar, + iconColor: AppColors.whiteColor, + iconSize: 20.h, + ), + SizedBox(height: 16.h), + ], + ).paddingSymmetrical(16.h, 0.h), + ), + SizedBox(height: 24.h), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/splashPage.dart b/lib/splashPage.dart index f85f01e..f363bc3 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -21,6 +21,7 @@ import 'package:provider/provider.dart'; import 'core/cache_consts.dart'; import 'core/utils/local_notifications.dart'; import 'core/utils/push_notification_handler.dart'; +import 'widgets/routes/custom_page_route.dart'; class SplashPage extends StatefulWidget { @override @@ -42,7 +43,7 @@ class _SplashScreenState extends State { Timer(Duration(seconds: 2, milliseconds: 500), () async { LocalNotification.init(onNotificationClick: (payload) {}); Navigator.of(context).pushReplacement( - FadePage( + CustomPageRoute( page: LandingNavigation(), // page: LoginScreen(), ), From c9206f1ab85aae159dc6f6a9fcc2c1bb0272da35 Mon Sep 17 00:00:00 2001 From: Haroon Amjad <> Date: Sat, 20 Sep 2025 20:24:39 +0300 Subject: [PATCH 4/4] Radiology report implemented --- assets/images/svg/home_lab_result_icon.svg | 4 + lib/core/api/api_client.dart | 4 +- lib/core/app_assets.dart | 1 + .../patient_radiology_response_model.dart | 6 +- lib/features/radiology/radiology_repo.dart | 101 +++++++++++ .../radiology/radiology_view_model.dart | 53 ++++++ .../home/data/landing_page_data.dart | 4 +- .../medical_file/medical_file_page.dart | 6 +- .../radiology/radiology_orders_page.dart | 4 +- .../radiology/radiology_result_page.dart | 164 +++++++++++++----- 10 files changed, 296 insertions(+), 51 deletions(-) create mode 100644 assets/images/svg/home_lab_result_icon.svg diff --git a/assets/images/svg/home_lab_result_icon.svg b/assets/images/svg/home_lab_result_icon.svg new file mode 100644 index 0000000..245048d --- /dev/null +++ b/assets/images/svg/home_lab_result_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index b0a5c78..a75a1ab 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -176,8 +176,8 @@ class ApiClientImp implements ApiClient { body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken; } - // body['TokenID'] = "@dm!n"; - // body['PatientID'] = "4767477"; + body['TokenID'] = "@dm!n"; + body['PatientID'] = 3628599; } body.removeWhere((key, value) => value == null); diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 0a414d0..aead424 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -131,6 +131,7 @@ class AppAssets { static const String smart_phone_fill = '$svgBasePath/smart_phone_fill.svg'; static const String touch_face_id = '$svgBasePath/touch_face_id.svg'; static const String minus = '$svgBasePath/minus.svg'; + static const String home_lab_result_icon = '$svgBasePath/home_lab_result_icon.svg'; //bottom navigation// static const String homeBottom = '$svgBasePath/home_bottom.svg'; diff --git a/lib/features/radiology/models/resp_models/patient_radiology_response_model.dart b/lib/features/radiology/models/resp_models/patient_radiology_response_model.dart index b5568bf..740c275 100644 --- a/lib/features/radiology/models/resp_models/patient_radiology_response_model.dart +++ b/lib/features/radiology/models/resp_models/patient_radiology_response_model.dart @@ -1,3 +1,5 @@ +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; + class PatientRadiologyResponseModel { String? setupID; int? projectID; @@ -6,7 +8,7 @@ class PatientRadiologyResponseModel { int? invoiceNo; int? doctorID; int? clinicID; - String? orderDate; + DateTime? orderDate; String? reportData; String? imageURL; String? procedureID; @@ -120,7 +122,7 @@ class PatientRadiologyResponseModel { invoiceNo = json['InvoiceNo']; doctorID = json['DoctorID']; clinicID = json['ClinicID']; - orderDate = json['OrderDate']; + orderDate = DateUtil.convertStringToDate(json['OrderDate']); reportData = json['ReportData']; imageURL = json['ImageURL']; procedureID = json['ProcedureID']; diff --git a/lib/features/radiology/radiology_repo.dart b/lib/features/radiology/radiology_repo.dart index 9db803d..0a44428 100644 --- a/lib/features/radiology/radiology_repo.dart +++ b/lib/features/radiology/radiology_repo.dart @@ -3,11 +3,17 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/api_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/authentication/models/resp_models/authenticated_user_resp_model.dart'; import 'package:hmg_patient_app_new/features/radiology/models/resp_models/patient_radiology_response_model.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; abstract class RadiologyRepo { Future>>> getPatientRadiologyOrders({required String patientId}); + + Future>> getRadiologyImage({required PatientRadiologyResponseModel patientRadiologyResponseModel}); + + Future>> getRadiologyReportPDF({required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser}); } class RadiologyRepoImp implements RadiologyRepo { @@ -58,4 +64,99 @@ class RadiologyRepoImp implements RadiologyRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>> getRadiologyImage({required PatientRadiologyResponseModel patientRadiologyResponseModel}) async { + Map mapDevice = { + "InvoiceNo": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? "0" : patientRadiologyResponseModel.invoiceNo, + "InvoiceNo_VP": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? patientRadiologyResponseModel.invoiceNo : "0", + "LineItemNo": patientRadiologyResponseModel.invoiceLineItemNo, + "ProjectID": patientRadiologyResponseModel.projectID!, + "InvoiceType": patientRadiologyResponseModel.invoiceType!, + "ExamId": patientRadiologyResponseModel.examId ?? "", + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + GET_RAD_IMAGE_URL, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response["Data"], + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future>> getRadiologyReportPDF({required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser}) async { + Map mapDevice = { + "InvoiceNo": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? 0 : patientRadiologyResponseModel.invoiceNo, + "InvoiceNo_VP": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? patientRadiologyResponseModel.invoiceNo : 0, + "LineItemNo": patientRadiologyResponseModel.invoiceLineItemNo, + "InvoiceLineItemNo": patientRadiologyResponseModel.invoiceLineItemNo, + "ProjectID": patientRadiologyResponseModel.projectID!, + "InvoiceType": patientRadiologyResponseModel.invoiceType!, + "SetupID": patientRadiologyResponseModel.setupID!, + // "ExamId": patientRadiologyResponseModel.examId ?? "", + "IsDownload": true, + 'ClinicName': patientRadiologyResponseModel.clinicDescription, + 'DateofBirth': authenticatedUser.dateofBirth, + 'DoctorName': patientRadiologyResponseModel.doctorName, + 'OrderDate': '${patientRadiologyResponseModel.orderDate!.year}-${patientRadiologyResponseModel.orderDate!.month}-${patientRadiologyResponseModel.orderDate!.day}', + 'PatientIditificationNum': authenticatedUser.patientIdentificationNo, + 'PatientMobileNumber': authenticatedUser.mobileNumber, + 'PatientName': "${authenticatedUser.firstName!} ${authenticatedUser.lastName!}", + 'ProjectName': patientRadiologyResponseModel.projectName, + 'RadResult': patientRadiologyResponseModel.reportData, + "To": authenticatedUser.emailAddress + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + SEND_RAD_REPORT_EMAIL, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response["Base64Data"], + ); + } 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/radiology/radiology_view_model.dart b/lib/features/radiology/radiology_view_model.dart index 1bdba04..3441881 100644 --- a/lib/features/radiology/radiology_view_model.dart +++ b/lib/features/radiology/radiology_view_model.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; @@ -6,17 +7,23 @@ import 'models/resp_models/patient_radiology_response_model.dart'; class RadiologyViewModel extends ChangeNotifier { bool isRadiologyOrdersLoading = false; + bool isRadiologyPDFReportLoading = false; RadiologyRepo radiologyRepo; ErrorHandlerService errorHandlerService; List patientRadiologyOrders = []; + String radiologyImageURL = ""; + String patientRadiologyReportPDFBase64 = ""; + RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService}); initRadiologyProvider() { patientRadiologyOrders.clear(); isRadiologyOrdersLoading = true; + isRadiologyPDFReportLoading = true; + radiologyImageURL = ""; getPatientRadiologyOrders(); notifyListeners(); } @@ -40,4 +47,50 @@ class RadiologyViewModel extends ChangeNotifier { }, ); } + + Future getRadiologyImage({required PatientRadiologyResponseModel patientRadiologyResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await radiologyRepo.getRadiologyImage(patientRadiologyResponseModel: patientRadiologyResponseModel); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + radiologyImageURL = apiResponse.data!; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future getRadiologyPDF( + {required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await radiologyRepo.getRadiologyReportPDF(patientRadiologyResponseModel: patientRadiologyResponseModel, authenticatedUser: authenticatedUser); + + result.fold( + (failure) async => await errorHandlerService.handleError( + failure: failure, + onOkPressed: () { + onError!(failure.message); + }, + ), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + onError!(apiResponse.errorMessage!); + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientRadiologyReportPDFBase64 = apiResponse.data!; + isRadiologyPDFReportLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } } diff --git a/lib/presentation/home/data/landing_page_data.dart b/lib/presentation/home/data/landing_page_data.dart index ad524e3..31b4598 100644 --- a/lib/presentation/home/data/landing_page_data.dart +++ b/lib/presentation/home/data/landing_page_data.dart @@ -80,7 +80,7 @@ class LandingPageData { ), ServiceCardData( serviceName: "lab_results", - icon: AppAssets.lab_result_icon, + icon: AppAssets.home_lab_result_icon, title: "My Lab", subtitle: "Results", backgroundColor: AppColors.whiteColor, @@ -90,7 +90,7 @@ class LandingPageData { ), ServiceCardData( serviceName: "radiology_results", - icon: AppAssets.lab_result_icon, + icon: AppAssets.home_lab_result_icon, title: "My Radiology", subtitle: "Results", backgroundColor: AppColors.whiteColor, diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 09ab37e..22de759 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -416,8 +416,8 @@ class _MedicalFilePageState extends State { children: [ Image.network( prescriptionVM.patientPrescriptionOrders[index].doctorImageURL!, - width: 63.h, - height: 63.h, + width: 40.h, + height: 40.h, fit: BoxFit.fill, ).circle(100), SizedBox(width: 16.h), @@ -442,7 +442,7 @@ class _MedicalFilePageState extends State { ], ), ), - SizedBox(width: 40.h), + // SizedBox(width: 40.h), Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor), ], ).onPress(() { diff --git a/lib/presentation/radiology/radiology_orders_page.dart b/lib/presentation/radiology/radiology_orders_page.dart index ad28a5a..c7732a4 100644 --- a/lib/presentation/radiology/radiology_orders_page.dart +++ b/lib/presentation/radiology/radiology_orders_page.dart @@ -119,9 +119,7 @@ class _RadiologyOrdersPageState extends State { children: [ AppCustomChipWidget( icon: AppAssets.doctor_calendar_icon, - labelText: model.isRadiologyOrdersLoading - ? "01 Jan 2025" - : DateUtil.formatDateToDate(DateUtil.convertStringToDate(model.patientRadiologyOrders[index].orderDate), false), + labelText: model.isRadiologyOrdersLoading ? "01 Jan 2025" : DateUtil.formatDateToDate(model.patientRadiologyOrders[index].orderDate!, false), ).toShimmer2(isShow: model.isRadiologyOrdersLoading), AppCustomChipWidget( labelText: model.isRadiologyOrdersLoading ? "01 Jan 2025" : model.patientRadiologyOrders[index].clinicDescription!, diff --git a/lib/presentation/radiology/radiology_result_page.dart b/lib/presentation/radiology/radiology_result_page.dart index cc4a4e7..f0f9af4 100644 --- a/lib/presentation/radiology/radiology_result_page.dart +++ b/lib/presentation/radiology/radiology_result_page.dart @@ -1,13 +1,24 @@ +import 'dart:async'; + 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/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/radiology/models/resp_models/patient_radiology_response_model.dart'; +import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/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'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:open_filex/open_filex.dart'; +import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher.dart'; class RadiologyResultPage extends StatefulWidget { RadiologyResultPage({super.key, required this.patientRadiologyResponseModel}); @@ -19,57 +30,132 @@ class RadiologyResultPage extends StatefulWidget { } class _RadiologyResultPageState extends State { + late RadiologyViewModel radiologyViewModel; + + @override + void initState() { + scheduleMicrotask(() { + radiologyViewModel.getRadiologyImage(patientRadiologyResponseModel: widget.patientRadiologyResponseModel); + }); + super.initState(); + } + @override Widget build(BuildContext context) { + radiologyViewModel = Provider.of(context); + AppState _appState = getIt.get(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, - body: CollapsingListView( - title: "Radiology Result".needTranslation, - child: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 24.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 24.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: true, - ), + body: Column( + children: [ + Expanded( + child: CollapsingListView( + title: "Radiology Result".needTranslation, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox(height: 16.h), - widget.patientRadiologyResponseModel.description!.toText16(isBold: true), - SizedBox(height: 8.h), - widget.patientRadiologyResponseModel.reportData!.trim().toText12(isBold: true, color: AppColors.textColorLight), - SizedBox(height: 16.h), - CustomButton( - text: "View Radiology Image".needTranslation, - onPressed: () async {}, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: AppColors.whiteColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - icon: AppAssets.calendar, - iconColor: AppColors.whiteColor, - iconSize: 20.h, + SizedBox(height: 24.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 16.h), + widget.patientRadiologyResponseModel.description!.toText16(isBold: true), + SizedBox(height: 8.h), + widget.patientRadiologyResponseModel.reportData!.trim().toText12(isBold: true, color: AppColors.textColorLight), + SizedBox(height: 16.h), + CustomButton( + text: "View Radiology Image".needTranslation, + 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".needTranslation); + } + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + icon: AppAssets.download, + iconColor: AppColors.whiteColor, + iconSize: 20.h, + ), + SizedBox(height: 16.h), + ], + ).paddingSymmetrical(16.h, 0.h), ), - SizedBox(height: 16.h), + SizedBox(height: 24.h), ], - ).paddingSymmetrical(16.h, 0.h), + ), ), - SizedBox(height: 24.h), - ], + ), + ), + ), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: true, ), + child: CustomButton( + text: "Download report".needTranslation, + onPressed: () async { + LoaderBottomSheet.showLoader(); + await radiologyViewModel.getRadiologyPDF(patientRadiologyResponseModel: widget.patientRadiologyResponseModel, authenticatedUser: _appState.getAuthenticatedUser()!, onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }).then((val) async { + LoaderBottomSheet.hideLoader(); + if (radiologyViewModel.patientRadiologyReportPDFBase64.isNotEmpty) { + String path = await Utils.createFileFromString(radiologyViewModel.patientRadiologyReportPDFBase64, "pdf"); + try { + OpenFilex.open(path); + } catch (ex) { + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: "Cannot open file".needTranslation), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + } + }); + }, + backgroundColor: AppColors.successColor, + borderColor: AppColors.successColor, + textColor: AppColors.whiteColor, + fontSize: 16, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 45.h, + icon: AppAssets.download, + iconColor: AppColors.whiteColor, + iconSize: 20.h, + ).paddingSymmetrical(24.h, 24.h), ), - ), + ], ), ); }