dev_sultan
Sultan khan 5 hours ago
commit be5b33ce78

@ -219,7 +219,7 @@
"unit": "الوحدة",
"request": "الطلبات",
"memberName": "اسم العضو",
"switchLogin": "عرض",
"switchLogin": "عرض الملف",
"removeMember": "إزالة",
"allowView": "السماح",
"rejectView": "رفض",
@ -1349,7 +1349,7 @@
"monthlyHealthSummaryReportDisclaimer": "يعكس تقرير الملخص الصحي الشهري هذا المؤشرات الصحية ونتائج التحليل لأحدث الزيارات. يرجى ملاحظة أن هذا سيتم إرساله تلقائيًا من النظام ولا يعتبر تقريرًا رسميًا لذا لا ينبغي اتخاذ أي قرار طبي بناءً عليه",
"updatingMonthlyReportStatus": "جاري تحديث حالة التقرير الشهري...",
"monthlyReportStatusUpdatedSuccessfully": "تم تحديث حالة التقرير الشهري بنجاح",
"whoCanViewMyMedicalFile": "من يمكنه عرض ملفي الطبي؟",
"whoCanViewMyMedicalFile": "من يمكنه الاطلاع على ملفي الطبي",
"acceptedYourRequestToBeYourFamilyMember": "{status} طلب الانضمام الى أفراد عائلتك",
"canViewYourFile": "يمكنه عرض ملفك",
"hasARequestPendingToBeYourFamilyMember": "لديه طلب {status} ليكون فردًا من عائلتك",
@ -1360,7 +1360,7 @@
"selectAProfile": "الرجاء تحديد ملف تعريف",
"switchFamilyFile": "قم بالتبديل من قائمة الملفات الطبية أدناه",
"medicalFiles": "الملفات الطبية",
"addANewFamilyMember": "إضافة فرد جديد",
"addANewFamilyMember": "اضافة",
"viewInvoiceDetails": "عرض تفاصيل الفاتورة",
"outPatient": "مريض خارجي",
"invoiceDetails": "تفاصيل الفاتورة",
@ -1664,6 +1664,7 @@
"sortByLocation": "الترتيب حسب الموقع",
"timeForFirstReminder": "اضافةتذكير",
"reminderRemovalNote": "يمكنك إلغاء التذكير لاحقاً من التقويم.",
"prescriptionDatePassedError": "تاريخ الوصفة الطبية قد انتهى. لا يمكنك إضافة تذكير لهذه الوصفة.",
"communicationLanguage": "لغة التواصل",
"cmcServiceHeader": "فحص صحي شامل: تشخيص متقدم، معلومات صحية مفصلة",
"cmcServiceDescription": "احصل على معلومات تفصيلية عن صحتك من خلال خدمات التشخيص المتقدمة لدينا. افهم جسمك بشكل أفضل لمستقبل صحي.",
@ -1702,7 +1703,7 @@
"doctorRemovedFromFavourite": "تمت إزالة من قائمة الأطباء المفضلين",
"addNewFavorite": "إضافة مفضلة جديدة",
"favourites": "المفضلة",
"pendingAncillaryOrders": "مدفوعات طلبات الاضافية",
"pendingAncillaryOrders": "طلبات غير مكتمله",
"liveCarePermissions": "يتطلب لايف كير أذونات الكاميرا والميكروفون وإشعارات، يرجى السماح لها بالمتابعة.",
"containsImage": "يحتوي على صورة",
"servicesSelected": "الخدمة / الخدمات",
@ -1810,7 +1811,6 @@
"watchUsOnYoutube": "شاهدنا على إنستغرام",
"connectOnLinkedin": "تواصل معنا على لينكد إن",
"riskFactorPageTitle": "اعراض تشعر بها",
"symptomsCheckSuggestions": "الاحتمالات",
"bloodDonationService": "التبرع \n بالدم",
"bookAppointmentService": "حجز\nموعد",
"pinchToZoom": "اضغط للتكبير",
@ -1818,5 +1818,11 @@
"zoomIntoDetails": "قم بالتكبير لرؤية التفاصيل",
"pinchWithTwoFingersToZoom": "اضغط بإصبعين للتكبير",
"dragToMoveAround": "اسحب للتحرك",
"startExploring": "ابدأ الاستكشاف"
"startExploring": "ابدأ الاستكشاف",
"symptomsCheckSuggestions": "الاحتمالات",
"appointmentArrived": "تم الحضور",
"calendarPermissionAlertForAppointment": "يرجى منح إذن الوصول إلى التقويم من إعدادات التطبيق لضبط تذكير الموعد",
"rechargePageKey": "اختر المستفيد",
"selectFamilyFile": "اختيار ",
"ancillaryOrdersListNew": "طلبات غير مكتملة"
}

@ -1652,6 +1652,7 @@
"calendarPermissionAlert": "Please grant calendar access permission from app settings to set medication reminder.",
"timeForFirstReminder": "Add Reminder",
"reminderRemovalNote": "You can remove it from your calendar later by switching off the reminder",
"prescriptionDatePassedError": "Prescription date has already passed. You cannot add a reminder for this prescription.",
"sortByLocation": "Sort by location",
"sortByNearestLocation": "Sort by nearest to your location",
"giveLocationPermissionForNearestList": "Please grant location permission from app settings to see the nearest locations.",
@ -1800,7 +1801,6 @@
"watchUsOnYoutube": "Watch us on Instagram",
"connectOnLinkedin": "Connect on LinkedIn",
"riskFactorPageTitle": "What do you feel?",
"symptomsCheckSuggestions": "Suggestions",
"bloodDonationService": "Blood Donation",
"bookAppointmentService": "Book\nAppointment",
"pinchToZoom": "Pinch to zoom",
@ -1808,7 +1808,13 @@
"zoomIntoDetails": "Zoom into details",
"pinchWithTwoFingersToZoom": "Pinch with two fingers to zoom",
"dragToMoveAround": "Drag to move around",
"startExploring": "Start exploring"
"startExploring": "Start exploring",
"symptomsCheckSuggestions": "Suggestions",
"appointmentArrived": "Arrived",
"calendarPermissionAlertForAppointment": "Please grant calendar access permission from app settings to set appointment reminder.",
"rechargePageKey": "Recharge Wallet",
"selectFamilyFile": "Select",
"ancillaryOrdersListNew": "Ancillary Orders List"
}

@ -126,8 +126,7 @@ class CalenderUtilsNew {
String? prescriptionNumber,
DateTime? scheduleDateTime,
}) async {
DateTime currentDay = DateTime.now();
DateTime actualDate = scheduleDateTime ?? DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, 8, 0);
DateTime startTimeOfDay = 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);
@ -139,28 +138,29 @@ class CalenderUtilsNew {
// Validate that prescription is still active
if (remainingDays <= 0) {
onFailure?.call("Prescription date has already passed. You cannot add a reminder for this prescription.");
onFailure?.call(LocaleKeys.prescriptionDatePassedError.tr());
return false;
}
bool statusOfOperation = false;
for (int i = 0; i < remainingDays; i++) {
//event for number of days.
// Reset to the original time for each new day
DateTime actualDate = startTimeOfDay.add(Duration(days: i));
//event for number of days (frequency per day)
for (int j = 0; j < frequencyNumber; j++) {
statusOfOperation = await createOrUpdateEvent(
title: "$itemDescriptionN} Medication about to due for prescription , #$prescriptionNumber",
title: "$itemDescriptionN Medication about to due for prescription, #$prescriptionNumber",
description: "$itemDescriptionN $frequencyNumber $route ",
scheduleDateTime: actualDate,
location: '',
//event id with varitions
//event id with variations
reminderMinutes: reminderMinutes);
if (!statusOfOperation) return false;
actualDate = actualDate.add(Duration(hours: interval));
// if (actualDate.difference(currentDay).inDays == 1) break;
}
actualDate = actualDate.add(Duration(days: 1));
}
return statusOfOperation;
@ -171,14 +171,17 @@ class CalenderUtilsNew {
}
FutureOr<List<Event>> getEvents() async {
var availableCalender = writableCalender.first;
var availableCalender = writableCalender.first;
DateTime startEventsDate = Jiffy.parseFromDateTime(DateTime.now()).subtract(days: 30).dateTime;
DateTime endEventsDate = Jiffy.parseFromDateTime(DateTime.now()).add(days: 120).dateTime;
return await calender.listEvents(startEventsDate, endEventsDate, calendarIds: [availableCalender.id]);
}
FutureOr<bool> checkIfEventExist(String admissionId) async {
await ensureCalendarsLoaded();
// await ensureCalendarsLoaded();
if (writableCalender.isEmpty) {
await ensureCalendarsLoaded();
}
List<Event> events = await getEvents();
if (events.isEmpty) return false;
for (var event in events) {

@ -231,30 +231,31 @@ class BloodDonationViewModel extends ChangeNotifier {
}
Future<bool> validateSelections() async {
AppState appState = getIt<AppState>();
if (selectedCity == null) {
await dialogService.showErrorBottomSheet(
message: "Please choose city",
message: appState.isArabic() ? "اختر المدينة" :"Please choose city",
);
return false;
}
if (selectedGender == null) {
await dialogService.showErrorBottomSheet(
message: "Please choose Gender",
message: appState.isArabic() ? "اختير الجنس" :"Please choose Gender",
);
return false;
}
if (selectedBloodType == null) {
await dialogService.showErrorBottomSheet(
message: "Please choose Blood Group",
message: appState.isArabic() ? "اختير فصيلة الدم" :"Please choose Blood Group",
);
return false;
}
if (!isTermsAccepted) {
await dialogService.showErrorBottomSheet(
message: "Please accept Terms and Conditions to continue",
message:appState.isArabic() ? "يرجى الموافقة على الشروط والأحكام للمتابعة" : "Please accept Terms and Conditions to continue",
);
return false;
}

@ -37,7 +37,7 @@ class AppointmentType {
case 42:
return LocaleKeys.confirmed.tr();
case 43:
return LocaleKeys.arrived.tr();
return LocaleKeys.appointmentArrived.tr();
default:
return LocaleKeys.booked.tr();
}

@ -74,7 +74,7 @@ class PrescriptionsViewModel extends ChangeNotifier {
Future<bool> checkIfReminderExistForPrescription(int index) async {
prescriptionDetailsList[index].hasReminder = await CalenderUtilsNew.instance.checkIfEventExist(prescriptionDetailsList[index].itemID?.toString() ?? "");
notifyListeners();
// notifyListeners();
return prescriptionDetailsList[index].hasReminder ?? false;
}
@ -226,7 +226,11 @@ class PrescriptionsViewModel extends ChangeNotifier {
}
showError(String errorMessage) {
getIt<DialogService>().showErrorBottomSheet(message: errorMessage);
getIt<NavigationService>().pop();
Future.delayed(Duration(milliseconds: 500), (){
getIt<DialogService>().showErrorBottomSheet(message: errorMessage);
});
}
Future<void> submitPrescriptionDeliveryRequest(

@ -1658,6 +1658,7 @@ abstract class LocaleKeys {
static const sortByLocation = 'sortByLocation';
static const timeForFirstReminder = 'timeForFirstReminder';
static const reminderRemovalNote = 'reminderRemovalNote';
static const prescriptionDatePassedError = 'prescriptionDatePassedError';
static const communicationLanguage = 'communicationLanguage';
static const cmcServiceHeader = 'cmcServiceHeader';
static const cmcServiceDescription = 'cmcServiceDescription';
@ -1803,5 +1804,10 @@ abstract class LocaleKeys {
static const connectOnLinkedin = 'connectOnLinkedin';
static const riskFactorPageTitle = 'riskFactorPageTitle';
static const symptomsCheckSuggestions = 'symptomsCheckSuggestions';
static const appointmentArrived = 'appointmentArrived';
static const calendarPermissionAlertForAppointment = 'calendarPermissionAlertForAppointment';
static const rechargePageKey = 'rechargePageKey';
static const selectFamilyFile = 'selectFamilyFile';
static const ancillaryOrdersListNew = 'ancillaryOrdersListNew';
}

@ -478,6 +478,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
});
},
isForAppointment: true
);
// If permission was not granted, revert the switch back to OFF

@ -62,7 +62,7 @@ class _RechargeWalletPageState extends State<RechargeWalletPage> {
children: [
Expanded(
child: CollapsingListView(
title: LocaleKeys.createAdvancedPayment.tr(context: context),
title: LocaleKeys.rechargePageKey.tr(context: context),
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(24.h),

@ -632,7 +632,7 @@ class _ServicesPageState extends State<ServicesPage> {
iconSize: 24.h,
iconColor: AppColors.primaryRedColor,
textColor: AppColors.primaryRedColor,
text: LocaleKeys.addMember.tr(),
text: getIt.get<AppState>().isArabic() ? LocaleKeys.add.tr() :LocaleKeys.addMember.tr(),
borderWidth: 0.w,
isBold: true,
borderColor: Colors.transparent,

@ -1219,7 +1219,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
);
}),
MedicalFileCard(
label: LocaleKeys.ancillaryOrdersList.tr(context: context),
label: LocaleKeys.ancillaryOrdersListNew.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.ancillary_orders_list_icon,

@ -1,3 +1,5 @@
import 'dart:io' show Platform;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
@ -8,12 +10,14 @@ 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/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/permission_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
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';
import 'package:permission_handler/permission_handler.dart';
class PrescriptionItemView extends StatelessWidget {
int index;
@ -233,4 +237,71 @@ class PrescriptionItemView extends StatelessWidget {
],
);
}
/// Checks and requests calendar permissions for both Android and iOS
/// Returns true if all required permissions are granted
Future<bool> _checkAndRequestCalendarPermissions() async {
if (Platform.isAndroid) {
// Android: Check existing permission first using PermissionService
bool hasPermission = await PermissionService.isCalendarPermissionEnabled();
if (hasPermission) {
return true;
}
// Request calendarFullAccess permission (Android 14+ requires this)
// This internally requests both READ_CALENDAR and WRITE_CALENDAR
PermissionStatus status = await Permission.calendarFullAccess.request();
// If calendarFullAccess request didn't show dialog or failed,
// fallback to requesting individual permissions
if (!status.isGranted) {
Map<Permission, PermissionStatus> statuses = await [
Permission.calendarWriteOnly,
].request();
return statuses[Permission.calendarWriteOnly]?.isGranted ?? false;
}
return status.isGranted;
} else {
// iOS: Check if we already have full access
PermissionStatus fullAccessStatus = await Permission.calendarFullAccess.status;
if (fullAccessStatus.isGranted) {
return true;
}
// Request write permission first (required before full access on iOS)
PermissionStatus writeStatus = await Permission.calendarWriteOnly.request();
if (!writeStatus.isGranted) {
return false;
}
// Then request full access permission
PermissionStatus fullStatus = await Permission.calendarFullAccess.request();
return fullStatus.isGranted;
}
}
/// Shows permission denied dialog with option to open settings
Future<void> _showPermissionDeniedDialog(BuildContext context) async {
showCommonBottomSheetWithoutHeight(
context,
title: LocaleKeys.notice.tr(context: context),
child: Utils.getWarningWidget(
loadingText: LocaleKeys.calendarPermissionAlert.tr(),
isShowActionButtons: true,
onCancelTap: () {
Navigator.of(context).pop();
},
onConfirmTap: () async {
Navigator.of(context).pop();
await openAppSettings();
},
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}

@ -18,7 +18,7 @@ import 'package:permission_handler/permission_handler.dart';
class BottomSheetUtils {
Future<bool> showReminderBottomSheet(BuildContext context, DateTime dateTime, String doctorName, String eventId, String appoDateFormatted, String appoTimeFormatted,
{required Function() onSuccess, String? title, String? description, Function(int)? onMultiDateSuccess, bool isMultiAllowed = false}) async {
{required Function() onSuccess, String? title, String? description, Function(int)? onMultiDateSuccess, bool isMultiAllowed = false, bool isForAppointment = false}) async {
// Check and request permissions based on platform
bool hasPermission = await _checkAndRequestCalendarPermissions();
@ -79,7 +79,7 @@ class BottomSheetUtils {
}
/// Shows permission denied dialog with option to open settings
Future<void> _showPermissionDeniedDialog() async {
Future<void> _showPermissionDeniedDialog({bool isForAppointment = false}) async {
BuildContext? context = GetIt.instance<NavigationService>().navigatorKey.currentContext;
if (context == null) return;
@ -87,7 +87,7 @@ class BottomSheetUtils {
context,
title: LocaleKeys.notice.tr(context: context),
child: Utils.getWarningWidget(
loadingText: LocaleKeys.calendarPermissionAlert.tr(),
loadingText: isForAppointment ? LocaleKeys.calendarPermissionAlertForAppointment.tr() : LocaleKeys.calendarPermissionAlert.tr(),
isShowActionButtons: true,
onCancelTap: () {
GetIt.instance<NavigationService>().pop();

Loading…
Cancel
Save