From 8b829b9101d5b64fdcdea9e9201b479ece9f3743 Mon Sep 17 00:00:00 2001 From: tahaalam Date: Tue, 31 Mar 2026 13:32:22 +0300 Subject: [PATCH] reminder start time added to the prescription --- assets/langs/ar-SA.json | 4 +- assets/langs/en-US.json | 4 +- lib/core/utils/calender_utils_new.dart | 6 +- .../prescriptions_view_model.dart | 8 + lib/generated/locale_keys.g.dart | 2 + .../prescriptions/prescription_item_view.dart | 121 ++++++++---- lib/widgets/scroll_wheel_time_picker.dart | 181 ++++++++++++++++++ 7 files changed, 288 insertions(+), 38 deletions(-) create mode 100644 lib/widgets/scroll_wheel_time_picker.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index b78b8545..e36c8278 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -1591,5 +1591,7 @@ "verifyInsurance": "التحقق من التأمين", "tests": "تحليل", "calendarPermissionAlert": "يرجى منح إذن الوصول إلى التقويم من إعدادات التطبيق لضبط تذكير تناول الدواء.", - "sortByLocation": "الترتيب حسب الموقع" + "sortByLocation": "الترتيب حسب الموقع", + "timeForFirstReminder": "وقت التذكير الأول", + "reminderRemovalNote": "يمكنك إزالتها من التقويم الخاص بك لاحقاً عن طريق إيقاف تشغيل التذكير" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index cbea7b94..33091c57 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -1585,5 +1585,7 @@ "verifyInsurance": "Verify Insurance", "tests": "tests", "calendarPermissionAlert": "Please grant calendar access permission from app settings to set medication reminder.", - "sortByLocation": "Sort by location" + "sortByLocation": "Sort by location", + "timeForFirstReminder": "Time for 1st reminder", + "reminderRemovalNote": "You can remove it from your calendar later by switching off the reminder" } diff --git a/lib/core/utils/calender_utils_new.dart b/lib/core/utils/calender_utils_new.dart index 331cf6d5..6f043e26 100644 --- a/lib/core/utils/calender_utils_new.dart +++ b/lib/core/utils/calender_utils_new.dart @@ -101,9 +101,11 @@ class CalenderUtilsNew { required String itemDescriptionN, required String route, Function(String)? onFailure, - String? prescriptionNumber}) async { + String? prescriptionNumber, + DateTime? scheduleDateTime, + }) async { DateTime currentDay = DateTime.now(); - DateTime actualDate = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, 8, 0); + DateTime actualDate = scheduleDateTime??DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, 8, 0); print("the frequency is $frequencyNumber"); frequencyNumber ??= 2; //Some time frequency number is null so by default will be 2 int interval = calculateIntervalAsPerFrequency(frequencyNumber); diff --git a/lib/features/prescriptions/prescriptions_view_model.dart b/lib/features/prescriptions/prescriptions_view_model.dart index 4baf6882..b9c29072 100644 --- a/lib/features/prescriptions/prescriptions_view_model.dart +++ b/lib/features/prescriptions/prescriptions_view_model.dart @@ -52,6 +52,8 @@ class PrescriptionsViewModel extends ChangeNotifier { bool isPrescriptionsDataNeedsReloading = true; List prescriptionsOrderList = []; + DateTime? selectedReminderTime; + PrescriptionsViewModel({required this.prescriptionsRepo, required this.errorHandlerService, required this.navServices}); initPrescriptionsViewModel() { @@ -71,6 +73,7 @@ class PrescriptionsViewModel extends ChangeNotifier { Future checkIfReminderExistForPrescription(int index) async { prescriptionDetailsList[index].hasReminder = await CalenderUtilsNew.instance.checkIfEventExist(prescriptionDetailsList[index].itemID?.toString() ?? ""); + notifyListeners(); return prescriptionDetailsList[index].hasReminder ?? false; } @@ -308,4 +311,9 @@ class PrescriptionsViewModel extends ChangeNotifier { }, ); } + + void serSelectedTime(DateTime dateTime) { + selectedReminderTime = dateTime; + notifyListeners(); + } } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 4f66d29c..f8a6c41e 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1586,5 +1586,7 @@ abstract class LocaleKeys { static const tests = 'tests'; static const calendarPermissionAlert = 'calendarPermissionAlert'; static const sortByLocation = 'sortByLocation'; + static const timeForFirstReminder = 'timeForFirstReminder'; + static const reminderRemovalNote = 'reminderRemovalNote'; } diff --git a/lib/presentation/prescriptions/prescription_item_view.dart b/lib/presentation/prescriptions/prescription_item_view.dart index 7f969b21..1c145115 100644 --- a/lib/presentation/prescriptions/prescription_item_view.dart +++ b/lib/presentation/prescriptions/prescription_item_view.dart @@ -13,6 +13,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/scroll_wheel_time_picker.dart'; class PrescriptionItemView extends StatelessWidget { int index; @@ -110,51 +111,60 @@ class PrescriptionItemView extends StatelessWidget { if (await prescriptionVM.checkIfReminderExistForPrescription(index)) { prescriptionVM.prescriptionDetailsList[index].hasReminder = true; - if (prescriptionVM.prescriptionDetailsList[index].hasReminder ?? false) { + LoaderBottomSheet.showLoader(loadingText: "Removing Reminders"); bool resultValue = await calender.checkAndRemoveMultipleItems(id: prescriptionVM.prescriptionDetailsList[index].itemID.toString()); prescriptionVM.setPrescriptionItemReminder(newValue, prescriptionVM.prescriptionDetailsList[index]); LoaderBottomSheet.hideLoader(); return; - } + } else { DateTime startDate = DateTime.now(); + prescriptionVM.serSelectedTime(startDate); DateTime endDate = DateTime(startDate.year, startDate.month, startDate.day + prescriptionVM.prescriptionDetailsList[index].days!.toInt()); - BottomSheetUtils().showReminderBottomSheet( + showCommonBottomSheetWithoutHeight( context, - endDate, - "", - prescriptionVM.prescriptionDetailsList[index].itemID.toString(), - "", - "", - title: "${prescriptionVM.prescriptionDetailsList[index].itemDescription} Prescription Reminder", - description: - "${prescriptionVM.prescriptionDetailsList[index].itemDescription} ${prescriptionVM.prescriptionDetailsList[index].frequency} ${prescriptionVM.prescriptionDetailsList[index].route} ", - onSuccess: () {}, - isMultiAllowed: true, - onMultiDateSuccess: (int selectedIndex) async { - bool isEventAdded = await calender.createMultipleEvents( - reminderMinutes: selectedIndex, - frequencyNumber: prescriptionVM.prescriptionDetailsList[index].frequencyNumber?.toInt(), - days: prescriptionVM.prescriptionDetailsList[index].days!.toInt(), - orderDate: prescriptionVM.prescriptionDetailsList[index].orderDate!, - itemDescriptionN: prescriptionVM.prescriptionDetailsList[index].itemDescription!, - route: prescriptionVM.prescriptionDetailsList[index].route!, - onFailure: (errorMessage) => prescriptionVM.showError(errorMessage), - prescriptionNumber: prescriptionVM.prescriptionDetailsList[index].itemID.toString(), - ); - prescriptionVM.setPrescriptionItemReminder(isEventAdded, prescriptionVM.prescriptionDetailsList[index]); - // setCalender(context, - // eventId: prescriptionVM.prescriptionDetailsList[index].itemID.toString(), - // selectedMinutes: selectedIndex, - // frequencyNumber: prescriptionVM.prescriptionDetailsList[index].frequencyNumber?.toInt(), - // days: prescriptionVM.prescriptionDetailsList[index].days!.toInt(), - // orderDate: prescriptionVM.prescriptionDetailsList[index].orderDate!, - // itemDescriptionN: prescriptionVM.prescriptionDetailsList[index].itemDescription!, - // route: prescriptionVM.prescriptionDetailsList[index].route!); - }, + child: bottomSheetContent(context), + title: LocaleKeys.timeForFirstReminder.tr(), + + isCloseButtonVisible: true ); + + // BottomSheetUtils().showReminderBottomSheet( + // context, + // endDate, + // "", + // prescriptionVM.prescriptionDetailsList[index].itemID.toString(), + // "", + // "", + // title: "${prescriptionVM.prescriptionDetailsList[index].itemDescription} Prescription Reminder", + // description: + // "${prescriptionVM.prescriptionDetailsList[index].itemDescription} ${prescriptionVM.prescriptionDetailsList[index].frequency} ${prescriptionVM.prescriptionDetailsList[index].route} ", + // onSuccess: () {}, + // isMultiAllowed: true, + // onMultiDateSuccess: (int selectedIndex) async { + // bool isEventAdded = await calender.createMultipleEvents( + // reminderMinutes: selectedIndex, + // frequencyNumber: prescriptionVM.prescriptionDetailsList[index].frequencyNumber?.toInt(), + // days: prescriptionVM.prescriptionDetailsList[index].days!.toInt(), + // orderDate: prescriptionVM.prescriptionDetailsList[index].orderDate!, + // itemDescriptionN: prescriptionVM.prescriptionDetailsList[index].itemDescription!, + // route: prescriptionVM.prescriptionDetailsList[index].route!, + // onFailure: (errorMessage) => prescriptionVM.showError(errorMessage), + // prescriptionNumber: prescriptionVM.prescriptionDetailsList[index].itemID.toString(), + // ); + // prescriptionVM.setPrescriptionItemReminder(isEventAdded, prescriptionVM.prescriptionDetailsList[index]); + // // setCalender(context, + // // eventId: prescriptionVM.prescriptionDetailsList[index].itemID.toString(), + // // selectedMinutes: selectedIndex, + // // frequencyNumber: prescriptionVM.prescriptionDetailsList[index].frequencyNumber?.toInt(), + // // days: prescriptionVM.prescriptionDetailsList[index].days!.toInt(), + // // orderDate: prescriptionVM.prescriptionDetailsList[index].orderDate!, + // // itemDescriptionN: prescriptionVM.prescriptionDetailsList[index].itemDescription!, + // // route: prescriptionVM.prescriptionDetailsList[index].route!); + // }, + // ); } }, ).toShimmer2(isShow: isLoading), @@ -186,4 +196,47 @@ class PrescriptionItemView extends StatelessWidget { ), ); } + + Widget bottomSheetContent(BuildContext context) { + return Column( + children: [ + SizedBox(height: 4.h), + LocaleKeys.reminderRemovalNote.tr().toText14(isBold: true, color: AppColors.warningColorYellow), + SizedBox(height: 8.h), + ScrollWheelTimePicker( + initialHour: DateTime.now().hour, + initialMinute: DateTime.now().minute, + use24HourFormat: true, + pickerHeight: 120.h, + itemExtent: 100.h, + onTimeChanged: (time) { + // Handle selected time + debugPrint('Selected time: ${time.hour}:${time.minute}'); + prescriptionVM.serSelectedTime(DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, time.hour, time.minute)); + }, + ), + SizedBox(height: 8.h), + CustomButton( + backgroundColor: AppColors.successColor, + borderColor: AppColors.successColor, + text: LocaleKeys.confirm.tr(), onPressed: () async { + CalenderUtilsNew calender = CalenderUtilsNew.instance; + bool isEventAdded = await calender.createMultipleEvents( + reminderMinutes: 10, + frequencyNumber: prescriptionVM.prescriptionDetailsList[index].frequencyNumber?.toInt(), + days: prescriptionVM.prescriptionDetailsList[index].days!.toInt(), + orderDate: prescriptionVM.prescriptionDetailsList[index].orderDate!, + itemDescriptionN: prescriptionVM.prescriptionDetailsList[index].itemDescription!, + route: prescriptionVM.prescriptionDetailsList[index].route!, + onFailure: (errorMessage) => prescriptionVM.showError(errorMessage), + prescriptionNumber: prescriptionVM.prescriptionDetailsList[index].itemID.toString(), + scheduleDateTime: prescriptionVM.selectedReminderTime, + ); + prescriptionVM.setPrescriptionItemReminder(isEventAdded, prescriptionVM.prescriptionDetailsList[index]); + Navigator.of(context).pop(); + }) + + ], + ); + } } diff --git a/lib/widgets/scroll_wheel_time_picker.dart b/lib/widgets/scroll_wheel_time_picker.dart new file mode 100644 index 00000000..a6e155bb --- /dev/null +++ b/lib/widgets/scroll_wheel_time_picker.dart @@ -0,0 +1,181 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; + + +class ScrollWheelTimePicker extends StatefulWidget { + final int initialHour; + + final int initialMinute; + + final bool use24HourFormat; + + final ValueChanged? onTimeChanged; + + final double itemExtent; + + final double pickerHeight; + + final TextStyle? digitTextStyle; + + final TextStyle? separatorTextStyle; + + const ScrollWheelTimePicker({ + super.key, + this.initialHour = 8, + this.initialMinute = 15, + this.use24HourFormat = true, + this.onTimeChanged, + this.itemExtent = 60, + this.pickerHeight = 180, + this.digitTextStyle, + this.separatorTextStyle, + }); + + @override + State createState() => _ScrollWheelTimePickerState(); +} + +class _ScrollWheelTimePickerState extends State { + late FixedExtentScrollController _hourController; + late FixedExtentScrollController _minuteController; + late int _selectedHour; + late int _selectedMinute; + + int get _maxHour => widget.use24HourFormat ? 24 : 12; + + @override + void initState() { + super.initState(); + _selectedHour = widget.initialHour; + _selectedMinute = widget.initialMinute; + + final hourIndex = + widget.use24HourFormat ? _selectedHour : (_selectedHour == 0 ? 11 : _selectedHour - 1); + + _hourController = FixedExtentScrollController(initialItem: hourIndex); + _minuteController = FixedExtentScrollController(initialItem: _selectedMinute); + } + + @override + void dispose() { + _hourController.dispose(); + _minuteController.dispose(); + super.dispose(); + } + + void _notifyChange() { + widget.onTimeChanged?.call(TimeOfDay(hour: _selectedHour, minute: _selectedMinute)); + } + + TextStyle get _defaultDigitStyle => TextStyle( + fontSize: 72.f, + fontWeight: FontWeight.w800, + color: AppColors.textColor, + letterSpacing: 0, + ); + + TextStyle get _defaultSeparatorStyle => TextStyle( + fontSize: 40.f, + fontWeight: FontWeight.w800, + color: AppColors.textColor, + letterSpacing: 0, + ); + + @override + Widget build(BuildContext context) { + final digitStyle = widget.digitTextStyle ?? _defaultDigitStyle; + final separatorStyle = widget.separatorTextStyle ?? _defaultSeparatorStyle; + + return SizedBox( + height: widget.pickerHeight, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 150.w, + child: ListWheelScrollView.useDelegate( + controller: _hourController, + itemExtent: widget.itemExtent, + physics: const FixedExtentScrollPhysics(), + perspective: 0.005, + diameterRatio: 1.2, + onSelectedItemChanged: (index) { + setState(() { + _selectedHour = + widget.use24HourFormat ? index : index + 1; + }); + _notifyChange(); + }, + childDelegate: ListWheelChildBuilderDelegate( + childCount: _maxHour, + builder: (context, index) { + final hour = + widget.use24HourFormat ? index : index + 1; + final isSelected = hour == _selectedHour; + return Visibility( + visible: isSelected, + child: Center( + child: AnimatedDefaultTextStyle( + duration: const Duration(milliseconds: 200), + style: digitStyle.copyWith( + color: isSelected + ? AppColors.textColor + : AppColors.textColor.withValues(alpha: 0.3), + ), + child: Text(hour.toString().padLeft(2, '0')), + ), + )); + }, + ), + ), + ), + + Padding( + padding: EdgeInsets.symmetric(horizontal: 2.w), + child: Text(':', style: separatorStyle), + ), + + SizedBox( + width: 150.w, + child: ListWheelScrollView.useDelegate( + controller: _minuteController, + itemExtent: widget.itemExtent, + physics: const FixedExtentScrollPhysics(), + perspective: 0.005, + diameterRatio: 1.2, + onSelectedItemChanged: (index) { + setState(() { + _selectedMinute = index; + }); + _notifyChange(); + }, + childDelegate: ListWheelChildBuilderDelegate( + childCount: 60, + + builder: (context, index) { + final isSelected = index == _selectedMinute; + return Visibility( + visible: isSelected, + child: + Center( + child: AnimatedDefaultTextStyle( + duration: const Duration(milliseconds: 200), + style: digitStyle.copyWith( + color: isSelected + ? AppColors.textColor + : AppColors.transparent.withValues(alpha: 0.3), + ), + child: Text(index.toString().padLeft(2, '0')), + ), + )); + }, + ), + ), + ), + ], + ), + ); + } +}