diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index c70123d4..13f778ff 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -36,7 +36,7 @@ android { applicationId = "com.cloudsolutions.HMGPatientApp" // minSdk = 24 minSdk = 29 - targetSdk = 35 + targetSdk = 36 compileSdk = 36 // targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..360ab55d --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,26 @@ + + #ffffff + #ED1C2B + #444647 + #2E3039 + #2E3039 + #a5a5a5 + #e1e1e1 + #FFF + #1e1e1e + #ED1C2B + #F9FAFD + #F2FAFD + #EC2213 + #4D6E6E6E + #FFFFFFFF + #00000000 + #3db39e + #00000000 + #c7c7c7 + #4c4c4c + #4c4c4c + #a5a5a5 + #ED1C2B + #FFF + \ No newline at end of file diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index b2bc5c2d..c025a030 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -1968,5 +1968,6 @@ "notAvailable": "غير متاح", "hide": "إخفاء", "viewPatientQR": "عرض رمز الاستجابة السريعة للمريض", - "patientQR": "رمز الاستجابة السريعة للمريض" + "patientQR": "رمز الاستجابة السريعة للمريض", + "forText": "عن" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 0e3429e4..69e0d66d 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -1958,7 +1958,8 @@ "notAvailable": "Not Available", "hide": "Hide", "viewPatientQR": "View Patient QR Code", - "patientQR": "Patient QR Code" + "patientQR": "Patient QR Code", + "forText": "For" } diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 3bfbf6cb..3a5ff281 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -210,8 +210,8 @@ class ApiClientImp implements ApiClient { body['TokenID'] = "@dm!n"; } - // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 2122445; + body['TokenID'] = "@dm!n"; + // body['PatientID'] = 1307867; // body['PatientID'] = 53320; // body['PatientTypeID'] = 1; // body['PatientOutSA'] = 0; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 8f2deaa3..8f1455ad 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -5,7 +5,7 @@ import 'package:hmg_patient_app_new/core/enums.dart'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index e4f028d9..8ec370a4 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -404,6 +404,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { isWaitingAppointmentSelected = false; isPatientRescheduleAppointment = false; bodyTypes = [maleLaserCategory, femaleLaserCategory]; + selectedChiefComplaintID = 0; // getLocation(); notifyListeners(); } @@ -420,14 +421,14 @@ class BookAppointmentsViewModel extends ChangeNotifier { setIsNearestAppointmentSelected(bool isNearestAppointmentSelected) { this.isNearestAppointmentSelected = isNearestAppointmentSelected; if (isNearestAppointmentSelected) { - for (var group in doctorsListGrouped) { - group.sort((a, b) { - var aSlot = a.nearestFreeSlot; - var bSlot = b.nearestFreeSlot; - if (aSlot == null || bSlot == null) return 0; - return DateUtil.convertStringToDate(aSlot).compareTo(DateUtil.convertStringToDate(bSlot)); - }); - } + // for (var group in doctorsListGrouped) { + // group.sort((a, b) { + // var aSlot = a.nearestFreeSlot; + // var bSlot = b.nearestFreeSlot; + // if (aSlot == null || bSlot == null) return 0; + // return DateUtil.convertStringToDate(aSlot).compareTo(DateUtil.convertStringToDate(bSlot)); + // }); + // } } else { // for (var group in doctorsListGrouped) { // group.sort((a, b) { diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 51a687a8..536f3355 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1950,5 +1950,6 @@ abstract class LocaleKeys { static const hide = 'hide'; static const viewPatientQR = 'viewPatientQR'; static const patientQR = 'patientQR'; + static const forText = 'forText'; } diff --git a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart index b689162c..3a74306e 100644 --- a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart +++ b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart @@ -16,6 +16,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart' show RegionListItem; 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:lottie/lottie.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; @@ -117,6 +118,14 @@ class _RegionBottomSheetBodyState extends State { height: MediaQuery.of(context).size.height * 0.5, child: Column( children: [ + // AppCustomChipWidget( + // richText: Row( + // children: [ + // "${appState.getAuthenticatedUser()!.age} ".toText10(color: AppColors.blackColor, isBold: true), + // LocaleKeys.yearsOld.tr(context: context).toText10(color: AppColors.blackColor, isBold: true), + // ], + // ), + // ), // Consumer( // builder: (context, regionVM, _) { // return Padding( diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart index 8d44f377..1451ecb3 100644 --- a/lib/presentation/authentication/register.dart +++ b/lib/presentation/authentication/register.dart @@ -17,6 +17,9 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart' show CustomButton; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/date_range_selector/date_range_calender.dart'; +import 'package:hmg_patient_app_new/widgets/date_range_selector/models/calendar_design_type.dart'; import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:provider/provider.dart'; @@ -153,30 +156,56 @@ class _RegisterNew extends State { }, ).withVerticalPadding(8), Divider(height: 1), - TextInputWidget( - labelText: LocaleKeys.dob.tr(context: context), - hintText: "11 July, 1994", - controller: authVm.dobController, - focusNode: _dobFocusNode, - isEnable: true, - prefix: null, - isAllowRadius: true, - isBorderAllowed: false, - isAllowLeadingIcon: true, - padding: EdgeInsets.symmetric(vertical: 8.h), - leadingIcon: AppAssets.birthday_cake, - selectionType: SelectionTypeEnum.calendar, - onCalendarTypeChanged: authVm.onCalenderTypeChange, - onChange: (value) { - authVm.onDobChange(value); - // Clear error when user selects a date - authVm.clearDobError(); + GestureDetector( + onTap: () async { + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.selectDate.tr(context: context), + context, + child: DateRangeSelector( + onRangeSelected: (DateTime? start, DateTime? end) { + if (start != null) { + var picked = start.toString().split(" ").first; + // authVm.dobController.text = picked; + authVm.onDobChange(picked); + // Clear error when user selects a date + authVm.clearDobError(); + } + }, + designType: CalendarDesignType.designV2, + showCalendarToggle: true, + onCalendarTypeChanged: null, + btnTitle: LocaleKeys.done.tr(context: context), + ), + isFullScreen: false, + isCloseButtonVisible: true, + callBackFunc: () {}, + ); }, - fontFamily: "Poppins", - hasError: false, - // Don't show individual field border - errorMessage: authVm.dobError, // Show error message if exists - ).withVerticalPadding(8), + child: TextInputWidget( + labelText: LocaleKeys.dob.tr(context: context), + hintText: "11 July, 1994", + controller: authVm.dobController, + focusNode: _dobFocusNode, + isEnable: false, + prefix: null, + isAllowRadius: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + padding: EdgeInsets.symmetric(vertical: 8.h), + leadingIcon: AppAssets.birthday_cake, + selectionType: SelectionTypeEnum.calendar, + onCalendarTypeChanged: authVm.onCalenderTypeChange, + onChange: (value) { + authVm.onDobChange(value); + // Clear error when user selects a date + authVm.clearDobError(); + }, + fontFamily: "Poppins", + hasError: false, + // Don't show individual field border + errorMessage: authVm.dobError, // Show error message if exists + ).withVerticalPadding(8), + ), ], ), ), diff --git a/lib/presentation/book_appointment/review_appointment_page.dart b/lib/presentation/book_appointment/review_appointment_page.dart index b7fb6469..a7e01e4b 100644 --- a/lib/presentation/book_appointment/review_appointment_page.dart +++ b/lib/presentation/book_appointment/review_appointment_page.dart @@ -424,6 +424,7 @@ class _ReviewAppointmentPageState extends State { void initiateBookAppointment() async { // LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: bookAppointmentsViewModel.isPatientRescheduleAppointment ? LocaleKeys.reschedulingAppo.tr(context: context) : LocaleKeys.bookingYourAppointment.tr(context: context)); + Clarity.sendCustomEvent('Appointment_book_success'); LoaderBottomSheet.showLoader( loadingText: bookAppointmentsViewModel.isPatientRescheduleAppointment ? LocaleKeys.reschedulingAppo.tr(context: context) @@ -449,6 +450,7 @@ class _ReviewAppointmentPageState extends State { callBackFunc: () { bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false); bookAppointmentsViewModel.setIsLiveCareSchedule(false); + bookAppointmentsViewModel.setSelectedChiefComplaintID(0); Navigator.pushAndRemoveUntil( context, CustomPageRoute( @@ -524,6 +526,7 @@ class _ReviewAppointmentPageState extends State { child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h), callBackFunc: () { bookAppointmentsViewModel.setIsLiveCareSchedule(false); bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false); + bookAppointmentsViewModel.setSelectedChiefComplaintID(0); Navigator.pushAndRemoveUntil(context, CustomPageRoute(page: LandingNavigation()), (r) => false); }, isFullScreen: false, isCloseButtonVisible: false, isAutoDismiss: true); }); diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index cf1cd897..e0a810ed 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -1202,7 +1202,9 @@ class _SelectClinicPageState extends State { getTitle(AppointmentViaRegionViewmodel data) { if (data.selectedRegionId == null) { - return LocaleKeys.selectRegion.tr(context: context).toText20(isBold: true); + return Expanded( + child: "${LocaleKeys.selectRegion.tr(context: context)} ${LocaleKeys.forText.tr(context: context)} '${bookAppointmentsViewModel.selectedClinic.clinicDescription}'".toText20(isBold: true), + ); } else { return Transform.flip( flipX: data.isArabic ? true : false, diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart index 26ab5519..132cd46a 100644 --- a/lib/presentation/book_appointment/select_doctor_page.dart +++ b/lib/presentation/book_appointment/select_doctor_page.dart @@ -57,7 +57,8 @@ class _SelectDoctorPageState extends State { } else if (bookAppointmentsViewModel.isGetDocForHealthCal) { bookAppointmentsViewModel.getDoctorsListByHealthCal(); } else { - bookAppointmentsViewModel.getDoctorsList(); + bookAppointmentsViewModel.setIsNearestAppointmentSelected(true); + bookAppointmentsViewModel.getDoctorsList(isNearest: true); } } }); diff --git a/lib/presentation/book_appointment/widgets/appointment_calendar.dart b/lib/presentation/book_appointment/widgets/appointment_calendar.dart index ea954e03..11092c79 100644 --- a/lib/presentation/book_appointment/widgets/appointment_calendar.dart +++ b/lib/presentation/book_appointment/widgets/appointment_calendar.dart @@ -14,8 +14,10 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/dental_chief_complaints_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/review_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/waiting_appointment/waiting_appointment_info.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; @@ -95,63 +97,116 @@ class _AppointmentCalendarState extends State { // ), SizedBox( height: MediaQuery.of(context).size.height * 0.45, - child: Directionality( - textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr, - child: Localizations.override( - context: context, - locale: isArabic ? const Locale('ar', 'SA') : const Locale('en'), - child: ClarityUnmask( - child: SfCalendar( - controller: _calendarController, - minDate: DateTime.now(), - showNavigationArrow: true, - // headerHeight: 60.h, - headerStyle: CalendarHeaderStyle( - backgroundColor: AppColors.transparent, - textAlign: isArabic ? TextAlign.end : TextAlign.start, - textStyle: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.primaryRedColor), - ), - viewHeaderStyle: ViewHeaderStyle( - // backgroundColor: AppColors.scaffoldBgColor, - dayTextStyle: TextStyle(fontSize: 14.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.textColor), - ), - view: CalendarView.month, - todayHighlightColor: Colors.transparent, - todayTextStyle: TextStyle(color: AppColors.textColor, fontWeight: FontWeight.bold), - selectionDecoration: ShapeDecoration( - color: AppColors.transparent, - shape: SmoothRectangleBorder( - borderRadius: BorderRadius.circular(10.r), - smoothness: 1, - side: BorderSide(color: AppColors.primaryRedColor, width: 1.5), + child: Stack( + children: [ + Directionality( + textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr, + child: Localizations.override( + context: context, + locale: isArabic ? const Locale('ar', 'SA') : const Locale('en'), + child: ClarityUnmask( + child: SfCalendar( + controller: _calendarController, + minDate: DateTime.now(), + initialDisplayDate: DateUtil.convertStringToDate(bookAppointmentsViewModel.freeSlotsResponse[0]), + showNavigationArrow: false, + headerHeight: 60.h, + headerStyle: CalendarHeaderStyle( + backgroundColor: AppColors.transparent, + // textAlign: isArabic ? TextAlign.end : TextAlign.start, + textStyle: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.primaryRedColor), + ), + viewHeaderStyle: ViewHeaderStyle( + // backgroundColor: AppColors.scaffoldBgColor, + dayTextStyle: TextStyle(fontSize: 14.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.textColor), + ), + view: CalendarView.month, + todayHighlightColor: Colors.transparent, + todayTextStyle: TextStyle(color: AppColors.textColor, fontWeight: FontWeight.bold), + selectionDecoration: ShapeDecoration( + color: AppColors.transparent, + shape: SmoothRectangleBorder( + borderRadius: BorderRadius.circular(10.r), + smoothness: 1, + side: BorderSide(color: AppColors.primaryRedColor, width: 1.5), + ), + ), + cellBorderColor: AppColors.transparent, + dataSource: MeetingDataSource(_getDataSource()), + monthCellBuilder: (context, details) => Padding( + padding: EdgeInsets.all(12.h), + child: details.date.day.toString().toText14( + isCenter: true, + color: details.date == _calendarController.selectedDate ? AppColors.primaryRedColor : AppColors.textColor, + isEnglishOnly: true, + ), + ), + monthViewSettings: MonthViewSettings( + dayFormat: "EEE", + appointmentDisplayMode: MonthAppointmentDisplayMode.indicator, + showTrailingAndLeadingDates: false, + appointmentDisplayCount: 1, + monthCellStyle: MonthCellStyle( + textStyle: TextStyle(fontSize: 19.f), + ), + ), + onTap: (CalendarTapDetails details) { + _calendarController.selectedDate = details.date; + _onDaySelected(details.date!); + }, ), ), - cellBorderColor: AppColors.transparent, - dataSource: MeetingDataSource(_getDataSource()), - monthCellBuilder: (context, details) => Padding( - padding: EdgeInsets.all(12.h), - child: details.date.day.toString().toText14( - isCenter: true, - color: details.date == _calendarController.selectedDate ? AppColors.primaryRedColor : AppColors.textColor, - isEnglishOnly: true, + ), + ), + // Custom Navigation Arrows + Positioned( + top: 0, + right: isArabic ? null : 0, + left: isArabic ? 0 : null, + child: SizedBox( + height: 60.h, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + // Previous Month Arrow + GestureDetector( + onTap: () { + setState(() { + _calendarController.backward!(); + }); + }, + child: Container( + padding: EdgeInsets.all(8.h), + child: Icon( + // isArabic ? Icons.arrow_forward_ios : Icons.arrow_back_ios, + Icons.arrow_back_ios, + size: 24.h, + color: AppColors.primaryRedColor, + ), ), + ), + // Next Month Arrow + GestureDetector( + onTap: () { + setState(() { + _calendarController.forward!(); + }); + }, + child: Container( + padding: EdgeInsets.all(8.h), + child: Icon( + // isArabic ? Icons.arrow_back_ios : Icons.arrow_forward_ios, + Icons.arrow_forward_ios, + size: 24.h, + color: AppColors.primaryRedColor, + ), + ), + ), + ], ), - monthViewSettings: MonthViewSettings( - dayFormat: "EEE", - appointmentDisplayMode: MonthAppointmentDisplayMode.indicator, - showTrailingAndLeadingDates: false, - appointmentDisplayCount: 1, - monthCellStyle: MonthCellStyle( - textStyle: TextStyle(fontSize: 19.f), - ), - ), - onTap: (CalendarTapDetails details) { - _calendarController.selectedDate = details.date; - _onDaySelected(details.date!); - }, ), ), - ), + ], ), ), SizedBox(height: 10.h), @@ -164,7 +219,7 @@ class _AppointmentCalendarState extends State { ? ClarityUnmask( child: ConstrainedBox( constraints: BoxConstraints( - maxHeight: MediaQuery.of(context).size.height * 0.15, + maxHeight: MediaQuery.of(context).size.height * 0.25, minHeight: 0, ), child: Directionality( @@ -213,15 +268,31 @@ class _AppointmentCalendarState extends State { ), ); } else { - bookAppointmentsViewModel.getAppointmentNearestGate( - projectID: bookAppointmentsViewModel.selectedDoctor.projectID!, clinicID: bookAppointmentsViewModel.selectedDoctor.clinicID!); - bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime, selectedDateDisplay); - Navigator.of(context).pop(); - Navigator.of(context).push( - CustomPageRoute( - page: ReviewAppointmentPage(), - ), - ); + if (bookAppointmentsViewModel.selectedClinic.clinicID! == 17 && bookAppointmentsViewModel.selectedChiefComplaintID == 0) { + GetClinicsListResponseModel selectedClinic = GetClinicsListResponseModel( + clinicID: bookAppointmentsViewModel.selectedClinic.clinicID, + clinicDescription: bookAppointmentsViewModel.selectedClinic.clinicDescription, + isLiveCareClinicAndOnline: false, + liveCareServiceID: 0, + liveCareClinicID: 0); + bookAppointmentsViewModel.setProjectID(bookAppointmentsViewModel.selectedDoctor.projectID.toString()); + bookAppointmentsViewModel.setSelectedClinic(selectedClinic); + bookAppointmentsViewModel.setIsChiefComplaintsListLoading(true); + Navigator.of(context).push( + CustomPageRoute( + page: DentalChiefComplaintsPage(), + ), + ); + } else { + bookAppointmentsViewModel.getAppointmentNearestGate(projectID: bookAppointmentsViewModel.selectedDoctor.projectID!, clinicID: bookAppointmentsViewModel.selectedDoctor.clinicID!); + bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime, selectedDateDisplay); + Navigator.of(context).pop(); + Navigator.of(context).push( + CustomPageRoute( + page: ReviewAppointmentPage(), + ), + ); + } } } else { showCommonBottomSheetWithoutHeight( @@ -231,8 +302,7 @@ class _AppointmentCalendarState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Lottie.asset(AppAnimations.errorAnimation, - repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), + Lottie.asset(AppAnimations.errorAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), SizedBox(height: 8.h), (LocaleKeys.loginToUseService.tr(context: context)).toText16(color: AppColors.blackColor), SizedBox(height: 16.h), @@ -331,12 +401,7 @@ class _AppointmentCalendarState extends State { selectedButtonIndex = 0; List> timeList = []; for (var i = 0; i < dayEvents.length; i++) { - Map timeSlot = { - "isoTime": dayEvents[i].isoTime, - "start": dayEvents[i].start.toString(), - "end": dayEvents[i].end.toString(), - "vidaDate": dayEvents[i].vidaDate - }; + Map timeSlot = {"isoTime": dayEvents[i].isoTime, "start": dayEvents[i].start.toString(), "end": dayEvents[i].end.toString(), "vidaDate": dayEvents[i].vidaDate}; timeList.add(timeSlot); } if (dayEvents.isNotEmpty) { diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 85f1e972..b3ba5e78 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -253,8 +253,8 @@ class _LandingPageState extends State { padding: EdgeInsets.only( top: (appState.isAuthenticated && !insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown) ? (MediaQuery.paddingOf(context).top + 70.h) - : kToolbarHeight + 0.h, - bottom: 24), + : kToolbarHeight + 16.h, + bottom: 24.h), child: Column( spacing: 16.h, children: [ diff --git a/lib/presentation/home/widgets/large_service_card.dart b/lib/presentation/home/widgets/large_service_card.dart index 7c037604..a0d9553d 100644 --- a/lib/presentation/home/widgets/large_service_card.dart +++ b/lib/presentation/home/widgets/large_service_card.dart @@ -128,7 +128,9 @@ class LargeServiceCard extends StatelessWidget { ), ], ), - ); + ).onPress(() { + handleOnTap(); + }); } void handleOnTap() { @@ -275,7 +277,9 @@ class FadedLargeServiceCard extends StatelessWidget { ), ], ), - ); + ).onPress(() { + handleOnTap(); + }); } void handleOnTap() { diff --git a/lib/presentation/home/widgets/welcome_widget.dart b/lib/presentation/home/widgets/welcome_widget.dart index 039da8c1..e3d96e3f 100644 --- a/lib/presentation/home/widgets/welcome_widget.dart +++ b/lib/presentation/home/widgets/welcome_widget.dart @@ -41,9 +41,13 @@ class WelcomeWidget extends StatelessWidget { borderRadius: BorderRadius.circular(30), child: Row( mainAxisSize: MainAxisSize.min, - spacing: 8.h, + // spacing: 8.h, children: [ - Icon(Icons.menu, color: AppColors.textColor).onPress(() { + SizedBox( + height: 40.h, + width: 20.w, + child: Icon(Icons.menu, color: AppColors.textColor), + ).onPress(() { getIt.get().getProfileSettings(); Navigator.of(context).push( CustomPageRoute( @@ -92,7 +96,7 @@ class WelcomeWidget extends StatelessWidget { ], ), ], - ).expanded, + ).paddingOnly(left: 8.h, right: 8.h).expanded, ], ), ), diff --git a/lib/presentation/lab/alphabeticScroll.dart b/lib/presentation/lab/alphabeticScroll.dart index 933f399c..ff0efbf4 100644 --- a/lib/presentation/lab/alphabeticScroll.dart +++ b/lib/presentation/lab/alphabeticScroll.dart @@ -133,7 +133,6 @@ class _AlphabetScrollPageState extends State { @override Widget build(BuildContext context) { - return SizedBox( width: MediaQuery.sizeOf(context).width, child: Row( @@ -149,7 +148,6 @@ class _AlphabetScrollPageState extends State { ], ), ), - SizedBox( width: 24.w, height: MediaQuery.sizeOf(context).height-(70.h+ kToolbarHeight+100.h), diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index c3a2adfe..0e8890ba 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -1 +1 @@ -import 'dart:async'; import 'dart:ui' as ui; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.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/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; import 'alphabeticScroll.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); rangeViewModel = Provider.of(context); _appState = getIt(); return CollapsingListView( title: LocaleKeys.labResults.tr(context: context), search: () async { if (labProvider.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: labProvider.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; labProvider.filterLabReports(value); } } }, child: Consumer( builder: (context, labViewModel, child) { return SingleChildScrollView( physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.all(24.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, LocaleKeys.byVisit.tr()), CustomTabBarModel(null, LocaleKeys.byTest.tr()), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), ), ], ), if (activeIndex == 0) Padding( padding: EdgeInsets.symmetric(vertical: 10.h), child: Row( children: [ // CustomButton( // text: LocaleKeys.byClinic.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(true); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, // borderColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), // textColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12.f, // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), // SizedBox(width: 8.h), // CustomButton( // text: LocaleKeys.byHospital.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(false); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, // borderColor: labViewModel.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, // textColor: labViewModel.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), ], ), ), SizedBox(height: 8.h), selectedFilterText!.isNotEmpty ? Column( children: [ AppCustomChipWidget( labelText: selectedFilterText!, backgroundColor: AppColors.alertColor, textColor: AppColors.whiteColor, deleteIcon: AppAssets.close_bottom_sheet_icon, deleteIconColor: AppColors.whiteColor, deleteIconHasColor: true, isEnglishOnly: true, onDeleteTap: () { selectedFilterText = ""; labProvider.filterLabReports(""); }, ), SizedBox(height: 8.h), ], ) : SizedBox(), activeIndex == 0 ? // By Visit - show grouped view when available labViewModel.isLabOrdersLoading ? ListView.builder( shrinkWrap: true, physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: 5, itemBuilder: (context, index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ), ) : (labViewModel.patientLabOrders.isNotEmpty ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: labViewModel.filteredLabOrders.length, itemBuilder: (context, index) { final group = labViewModel.filteredLabOrders[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: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ AppCustomChipWidget( richText: Row( children: [ "${group.testDetails!.length} ".toText10( color: AppColors.successColor, isEnglishOnly: true, weight: FontWeight.w600), LocaleKeys.tests .tr(context: context) .toText10(color: AppColors.successColor, weight: FontWeight.w600), ], ), labelText: "${group.testDetails!.length} ${LocaleKeys.tests.tr(context: context)}", backgroundColor: AppColors.successColor.withOpacity(0.1), textColor: AppColors.successColor, ), AppCustomChipWidget( labelText: "${_appState.isArabic() ? group.isInOutPatientDescriptionN : group.isInOutPatientDescription}", backgroundColor: AppColors.warningColorYellow.withOpacity(0.1), textColor: AppColors.warningColorYellow, ) ], ), Icon(isExpanded ? Icons.expand_less : Icons.expand_more), ], ), SizedBox(height: 8.h), Row( mainAxisSize: MainAxisSize.min, children: [ Image.network( group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", width: 24.w, height: 24.h, fit: BoxFit.cover, ).circle(100), SizedBox(width: 8.h), Expanded( child: (group.doctorName ?? group.doctorNameEnglish ?? "") .toString() .toText14(isBold: true)), ], ), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ Directionality( textDirection: ui.TextDirection.ltr, child: AppCustomChipWidget( labelText: DateUtil.formatDateToDate( DateUtil.convertStringToDate(group.orderDate ?? ""), false), isEnglishOnly: true, )), AppCustomChipWidget( labelText: (group.projectName ?? ""), ), AppCustomChipWidget( labelText: (group.clinicDescription ?? ""), ), ], ), ], ), ), AnimatedSwitcher( duration: Duration(milliseconds: 500), switchInCurve: Curves.easeIn, switchOutCurve: Curves.easeOut, transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: animation, child: SizeTransition( sizeFactor: animation, axisAlignment: 0.0, child: child, ), ); }, child: isExpanded ? Container( key: ValueKey(index), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 0.h), child: Column( children: [ ListView.separated( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemBuilder: (cxt, index) { PatientLabOrdersResponseModel order = group; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "• ${order.testDetails![index].description!}" .toText14(isBold: true, isEnglishOnly: true), SizedBox(height: 4.h), (_appState.isArabic() ? order.testDetails![index].testDescriptionAr! : order.testDetails![index].testDescriptionEn!).toText12( isBold: true, color: AppColors.textColorLight, isEnglishOnly: true), ], ).paddingOnly(top: 16, bottom: 16); }, separatorBuilder: (cxt, index) => Divider( color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), itemCount: group.testDetails!.length > 3 ? 3 : group.testDetails!.length), SizedBox(height: 16.h), CustomButton( text: "${LocaleKeys.viewResults.tr()} (${group.testDetails!.length})", onPressed: () { labProvider.currentlySelectedPatientOrder = group; labProvider.getPatientLabResultByHospital(group); labProvider.getPatientSpecialResult(group); Navigator.of(context).push( CustomPageRoute( page: LabResultByClinic(labOrder: group), ), ); }, backgroundColor: AppColors.infoColor.withAlpha(20), borderColor: AppColors.infoColor.withAlpha(0), textColor: AppColors.infoColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, isBold: true, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, iconSize: 14.h, icon: AppAssets.view_report_icon, iconColor: AppColors.infoColor, ), SizedBox(height: 16.h), ], ), ) : SizedBox.shrink(), ), ], ), ), ), ), )); }, ) : Utils.getNoDataWidget(context, noDataText: LocaleKeys.noLabResults.tr(context: context))) : // By Test or other tabs keep existing behavior (labViewModel.isLabOrdersLoading) ? Column( children: List.generate( 5, (index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, )), ) : AlphabeticScroll( alpahbetsAvailable: labViewModel.filteredIndexedCharacterForUniqueTest, details: labViewModel.filteredUniqueTestsList, labViewModel: labViewModel, rangeViewModel: rangeViewModel, appState: _appState, ) ], )); }, ), ); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file +import 'dart:async'; import 'dart:ui' as ui; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.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/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; import 'alphabeticScroll.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); rangeViewModel = Provider.of(context); _appState = getIt(); return CollapsingListView( title: LocaleKeys.labResults.tr(context: context), physics: activeIndex == 1 ? NeverScrollableScrollPhysics() : ScrollPhysics(), search: () async { if (labProvider.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: labProvider.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; labProvider.filterLabReports(value); } } }, child: Consumer( builder: (context, labViewModel, child) { return SingleChildScrollView( physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.all(24.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, LocaleKeys.byVisit.tr()), CustomTabBarModel(null, LocaleKeys.byTest.tr()), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), ), ], ), if (activeIndex == 0) Padding( padding: EdgeInsets.symmetric(vertical: 10.h), child: Row( children: [ // CustomButton( // text: LocaleKeys.byClinic.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(true); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, // borderColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), // textColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12.f, // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), // SizedBox(width: 8.h), // CustomButton( // text: LocaleKeys.byHospital.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(false); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, // borderColor: labViewModel.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, // textColor: labViewModel.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), ], ), ), SizedBox(height: 8.h), selectedFilterText!.isNotEmpty ? Column( children: [ AppCustomChipWidget( labelText: selectedFilterText!, backgroundColor: AppColors.alertColor, textColor: AppColors.whiteColor, deleteIcon: AppAssets.close_bottom_sheet_icon, deleteIconColor: AppColors.whiteColor, deleteIconHasColor: true, isEnglishOnly: true, onDeleteTap: () { selectedFilterText = ""; labProvider.filterLabReports(""); }, ), SizedBox(height: 8.h), ], ) : SizedBox(), activeIndex == 0 ? // By Visit - show grouped view when available labViewModel.isLabOrdersLoading ? ListView.builder( shrinkWrap: true, physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: 5, itemBuilder: (context, index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ), ) : (labViewModel.patientLabOrders.isNotEmpty ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: labViewModel.filteredLabOrders.length, itemBuilder: (context, index) { final group = labViewModel.filteredLabOrders[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: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ AppCustomChipWidget( richText: Row( children: [ "${group.testDetails!.length} ".toText10( color: AppColors.successColor, isEnglishOnly: true, weight: FontWeight.w600), LocaleKeys.tests .tr(context: context) .toText10(color: AppColors.successColor, weight: FontWeight.w600), ], ), labelText: "${group.testDetails!.length} ${LocaleKeys.tests.tr(context: context)}", backgroundColor: AppColors.successColor.withOpacity(0.1), textColor: AppColors.successColor, ), AppCustomChipWidget( labelText: "${_appState.isArabic() ? group.isInOutPatientDescriptionN : group.isInOutPatientDescription}", backgroundColor: AppColors.warningColorYellow.withOpacity(0.1), textColor: AppColors.warningColorYellow, ) ], ), Icon(isExpanded ? Icons.expand_less : Icons.expand_more), ], ), SizedBox(height: 8.h), Row( mainAxisSize: MainAxisSize.min, children: [ Image.network( group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", width: 24.w, height: 24.h, fit: BoxFit.cover, ).circle(100), SizedBox(width: 8.h), Expanded( child: (group.doctorName ?? group.doctorNameEnglish ?? "") .toString() .toText14(isBold: true)), ], ), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ Directionality( textDirection: ui.TextDirection.ltr, child: AppCustomChipWidget( labelText: DateUtil.formatDateToDate( DateUtil.convertStringToDate(group.orderDate ?? ""), false), isEnglishOnly: true, )), AppCustomChipWidget( labelText: (group.projectName ?? ""), ), AppCustomChipWidget( labelText: (group.clinicDescription ?? ""), ), ], ), ], ), ), AnimatedSwitcher( duration: Duration(milliseconds: 500), switchInCurve: Curves.easeIn, switchOutCurve: Curves.easeOut, transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: animation, child: SizeTransition( sizeFactor: animation, axisAlignment: 0.0, child: child, ), ); }, child: isExpanded ? Container( key: ValueKey(index), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 0.h), child: Column( children: [ ListView.separated( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemBuilder: (cxt, index) { PatientLabOrdersResponseModel order = group; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "• ${order.testDetails![index].description!}" .toText14(isBold: true, isEnglishOnly: true), SizedBox(height: 4.h), (_appState.isArabic() ? order.testDetails![index].testDescriptionAr! : order.testDetails![index].testDescriptionEn!).toText12( isBold: true, color: AppColors.textColorLight, isEnglishOnly: true), ], ).paddingOnly(top: 16, bottom: 16); }, separatorBuilder: (cxt, index) => Divider( color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), itemCount: group.testDetails!.length > 3 ? 3 : group.testDetails!.length), SizedBox(height: 16.h), CustomButton( text: "${LocaleKeys.viewResults.tr()} (${group.testDetails!.length})", onPressed: () { labProvider.currentlySelectedPatientOrder = group; labProvider.getPatientLabResultByHospital(group); labProvider.getPatientSpecialResult(group); Navigator.of(context).push( CustomPageRoute( page: LabResultByClinic(labOrder: group), ), ); }, backgroundColor: AppColors.infoColor.withAlpha(20), borderColor: AppColors.infoColor.withAlpha(0), textColor: AppColors.infoColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, isBold: true, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, iconSize: 14.h, icon: AppAssets.view_report_icon, iconColor: AppColors.infoColor, ), SizedBox(height: 16.h), ], ), ) : SizedBox.shrink(), ), ], ), ), ), ), )); }, ) : Utils.getNoDataWidget(context, noDataText: LocaleKeys.noLabResults.tr(context: context))) : // By Test or other tabs keep existing behavior (labViewModel.isLabOrdersLoading) ? Column( children: List.generate( 5, (index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, )), ) : AlphabeticScroll( alpahbetsAvailable: labViewModel.filteredIndexedCharacterForUniqueTest, details: labViewModel.filteredUniqueTestsList, labViewModel: labViewModel, rangeViewModel: rangeViewModel, appState: _appState, ) ], )); }, ), ); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart index f3eb94fb..82b62507 100644 --- a/lib/widgets/buttons/custom_button.dart +++ b/lib/widgets/buttons/custom_button.dart @@ -6,7 +6,7 @@ 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/theme/colors.dart'; -class CustomButton extends StatelessWidget { +class CustomButton extends StatefulWidget { final String text; final String? icon; final VoidCallback? onPressed; @@ -54,25 +54,61 @@ class CustomButton extends StatelessWidget { this.applyThemeColor = true, }); + @override + State createState() => _CustomButtonState(); +} + +class _CustomButtonState extends State with SingleTickerProviderStateMixin { + late AnimationController _animationController; + late Animation _scaleAnimation; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(milliseconds: 80), + vsync: this, + ); + _scaleAnimation = Tween(begin: 1.0, end: 0.95).animate( + CurvedAnimation(parent: _animationController, curve: Curves.easeInOut), + ); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + Future _playTapAnimation() async { + if (!widget.isDisabled && widget.onPressed != null) { + await _animationController.forward(); + _animationController.reverse(); + } + } + @override Widget build(BuildContext context) { - final radius = borderRadius ?? (12.r); - final iconS = iconSize ?? (24.h); - final fontS = fontSize ?? (16.f); - final isButtonDisabled = isDisabled || onPressed == null; + final radius = widget.borderRadius ?? (12.r); + final iconS = widget.iconSize ?? (24.h); + final fontS = widget.fontSize ?? (16.f); + final isButtonDisabled = widget.isDisabled || widget.onPressed == null; return Semantics( button: true, enabled: !isButtonDisabled, - label: text, - hint: isButtonDisabled ? 'Button is disabled' : 'Tap to $text', - child: GestureDetector( + label: widget.text, + hint: isButtonDisabled ? 'Button is disabled' : 'Tap to ${widget.text}', + child: GestureDetector( onTap: isButtonDisabled - ? null - : () { + ? null + : () async { + // Play tap animation and wait for it to complete + await _playTapAnimation(); + // Log button tap to Clarity before executing callback try { - Clarity.setCustomTag('button_text', text.length > 100 ? text.substring(0, 100) : text); + Clarity.setCustomTag('button_text', widget.text.length > 100 ? widget.text.substring(0, 100) : widget.text); Clarity.setCustomTag('button_action', 'tap'); Clarity.setCustomTag('button_disabled', 'false'); Clarity.sendCustomEvent('button_tapped'); @@ -81,67 +117,73 @@ class CustomButton extends StatelessWidget { } // Execute the actual button callback - onPressed?.call(); + widget.onPressed?.call(); }, - child: Container( - height: height == null - ? 56.h + child: AnimatedBuilder( + animation: _scaleAnimation, + builder: (context, child) => Transform.scale( + scale: _scaleAnimation.value, + child: child, + ), + child: Container( + height: widget.height == null + ? 56.h : (isFoldable || isTablet) - ? height! + 10.h - : height, - width: width, - padding: padding, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: isButtonDisabled ? AppColors.inputLabelTextColor.withValues(alpha: 0.3) : backgroundColor, - borderRadius: radius, + ? widget.height! + 10.h + : widget.height, + width: widget.width, + padding: widget.padding, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: isButtonDisabled ? AppColors.inputLabelTextColor.withValues(alpha: 0.3) : widget.backgroundColor, + borderRadius: radius, customBorder: BorderRadius.circular(radius), - side: borderSide ?? BorderSide(width: borderWidth.h, color: isButtonDisabled ? Colors.transparent : borderColor)), - child: Row( + side: widget.borderSide ?? BorderSide(width: widget.borderWidth.h, color: isButtonDisabled ? Colors.transparent : widget.borderColor)), + child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - if (icon != null) - Padding( - padding: text.isNotEmpty ? EdgeInsets.only(right: 6.w, left: 6.w) : EdgeInsets.zero, - child: Utils.buildSvgWithAssets( - icon: icon!, - iconColor: isButtonDisabled ? AppColors.greyTextColor : iconColor, - isDisabled: isButtonDisabled, + if (widget.icon != null) + Padding( + padding: widget.text.isNotEmpty ? EdgeInsets.only(right: 6.w, left: 6.w) : EdgeInsets.zero, + child: Utils.buildSvgWithAssets( + icon: widget.icon!, + iconColor: isButtonDisabled ? AppColors.greyTextColor : widget.iconColor, + isDisabled: isButtonDisabled, width: iconS, height: iconS, - applyThemeColor: applyThemeColor), - ), - if (text.isNotEmpty && icon != null) - Flexible( + applyThemeColor: widget.applyThemeColor), + ), + if (widget.text.isNotEmpty && widget.icon != null) + Flexible( child: Text( - text, - overflow: textOverflow ?? TextOverflow.ellipsis, - maxLines: 1, + widget.text, + overflow: widget.textOverflow ?? TextOverflow.ellipsis, + maxLines: 1, textAlign: TextAlign.center, style: context.dynamicTextStyle( fontSize: fontS, - color: isButtonDisabled ? AppColors.greyTextColor : textColor, - letterSpacing: 0, - fontWeight: isBold ? FontWeight.w700 : fontWeight, - ), + color: isButtonDisabled ? AppColors.greyTextColor : widget.textColor, + letterSpacing: 0, + fontWeight: widget.isBold ? FontWeight.w700 : widget.fontWeight, + ), ), ), - if (text.isNotEmpty && icon == null) - Text( - text, - overflow: textOverflow ?? TextOverflow.ellipsis, - maxLines: 1, + if (widget.text.isNotEmpty && widget.icon == null) + Text( + widget.text, + overflow: widget.textOverflow ?? TextOverflow.ellipsis, + maxLines: 1, textAlign: TextAlign.center, style: context.dynamicTextStyle( fontSize: fontS, - color: isButtonDisabled ? AppColors.greyTextColor : textColor, - letterSpacing: 0, - fontWeight: isBold ? FontWeight.w700 : fontWeight, - ), + color: isButtonDisabled ? AppColors.greyTextColor : widget.textColor, + letterSpacing: 0, + fontWeight: widget.isBold ? FontWeight.w700 : widget.fontWeight, + ), ), ], ), )), - ); + )); } } diff --git a/pubspec.yaml b/pubspec.yaml index bdce8130..006aca7f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -104,7 +104,7 @@ dependencies: scrollable_positioned_list: ^0.3.8 in_app_review: ^2.0.11 - flutter_paytabs_bridge: ^2.7.13 + flutter_paytabs_bridge: ^2.7.14 clarity_flutter: 1.9.0 flutter_amazonpaymentservices: ^0.0.13 screen_brightness: ^1.0.1