Compare commits
74 Commits
main
...
dev_sultan
| Author | SHA1 | Date |
|---|---|---|
|
|
7d8537fae6 | 10 hours ago |
|
|
1edeff439e | 10 hours ago |
|
|
a1beacc36e | 11 hours ago |
|
|
3e6ee8c10c | 22 hours ago |
|
|
87422e8e05 | 22 hours ago |
|
|
179b86b2b0 | 1 day ago |
|
|
f6aa1fc7ac | 1 day ago |
|
|
9795276ef9 | 1 day ago |
|
|
96ccb40f36 | 1 day ago |
|
|
bb7c05493a | 1 day ago |
|
|
169215fd7b | 1 day ago |
|
|
97a47ce4e8 | 1 day ago |
|
|
287e2b9562 | 1 day ago |
|
|
8b5c4d7575 | 1 day ago |
|
|
8c92df8648 | 1 day ago |
|
|
8f428297cb | 2 days ago |
|
|
e9d3b3680a | 2 days ago |
|
|
a0008c9104 | 2 days ago |
|
|
1d224702ac | 2 days ago |
|
|
69666a6f6c | 2 days ago |
|
|
475a9b9acb | 2 days ago |
|
|
06c67a0409 | 2 days ago |
|
|
d1b1bbf777 | 2 days ago |
|
|
0ddaa6899d | 3 days ago |
|
|
42222804fd | 3 days ago |
|
|
bf210e664d | 3 days ago |
|
|
6725ab028d | 3 days ago |
|
|
609ae6ab83 | 3 days ago |
|
|
344adf23c0 | 3 days ago |
|
|
44ce353dac | 3 days ago |
|
|
b5753769b7 | 3 days ago |
|
|
250af1506c | 3 days ago |
|
|
fc750174fe | 3 days ago |
|
|
d848291930 | 3 days ago |
|
|
aee496b7c3 | 3 days ago |
|
|
16da0f4d0a | 3 days ago |
|
|
8ee0e3b4ea | 3 days ago |
|
|
0c68ca4438 | 3 days ago |
|
|
e454c9c991 | 3 days ago |
|
|
96ebc3c7e9 | 3 days ago |
|
|
2240c6cc65 | 3 days ago |
|
|
0514716910 | 3 days ago |
|
|
e903f9c418 | 3 days ago |
|
|
ac0d72b3ff | 3 days ago |
|
|
f62c7c9b20 | 3 days ago |
|
|
c11e859df1 | 3 days ago |
|
|
91c16f13d1 | 3 days ago |
|
|
1c0842ab95 | 3 days ago |
|
|
02930a901e | 1 week ago |
|
|
20e4f82df3 | 1 week ago |
|
|
8ae81d2b24 | 1 week ago |
|
|
34e8386ca1 | 1 week ago |
|
|
cc255e9c9a | 1 week ago |
|
|
11de01e079 | 2 weeks ago |
|
|
62bb667cb8 | 2 weeks ago |
|
|
f3674ba06f | 3 weeks ago |
|
|
a53602dc8c | 3 weeks ago |
|
|
7a03242396 | 3 weeks ago |
|
|
68f044de52 | 3 weeks ago |
|
|
c9de23347a | 1 month ago |
|
|
190492a191 | 1 month ago |
|
|
3d8daeb58d | 1 month ago |
|
|
0b3fea230f | 1 month ago |
|
|
86a91ab383 | 2 months ago |
|
|
89ff10f506 | 2 months ago |
|
|
8191852f4d | 2 months ago |
|
|
e17d3cde87 | 2 months ago |
|
|
77242825bb | 2 months ago |
|
|
27ad4e0764 | 2 months ago |
|
|
eae16eec44 | 2 months ago |
|
|
3f123d12d6 | 3 months ago |
|
|
de8c7bc605 | 3 months ago |
|
|
cb56503622 | 3 months ago |
|
|
e8319a0d3f | 3 months ago |
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.98978 14.0362H21.2589C22.0592 14.0362 22.7076 13.3877 22.7076 12.5874C22.7076 11.7871 22.0592 11.1387 21.2589 11.1387H1.98978C1.18949 11.1387 0.541029 11.7871 0.541029 12.5874C0.541029 13.3877 1.18949 14.0362 1.98978 14.0362Z" fill="#2E3039"/>
|
||||
<path d="M14.6241 20.8879C14.9764 20.8879 15.3299 20.7598 15.6087 20.5013L22.9973 13.6488C23.2952 13.3723 23.4638 12.9835 23.4609 12.5767C23.458 12.1704 23.2847 11.7833 22.9828 11.511L15.3914 4.65838C14.7974 4.12234 13.8812 4.16928 13.3451 4.76327C12.8091 5.35726 12.856 6.27345 13.45 6.80949L19.8663 12.601L13.6384 18.3769C13.0519 18.921 13.0171 19.8378 13.5613 20.4243C13.847 20.732 14.2347 20.8879 14.6241 20.8879Z" fill="#2E3039"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 803 B |
@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.0102 14.0362H2.74111C1.94082 14.0362 1.29236 13.3877 1.29236 12.5874C1.29236 11.7871 1.94082 11.1387 2.74111 11.1387H22.0102C22.8105 11.1387 23.459 11.7871 23.459 12.5874C23.459 13.3877 22.8105 14.0362 22.0102 14.0362Z" fill="#2E3039"/>
|
||||
<path d="M9.3759 20.8879C9.02357 20.8879 8.67007 20.7598 8.39133 20.5013L1.0027 13.6488C0.704836 13.3723 0.536202 12.9835 0.539099 12.5767C0.541997 12.1704 0.715267 11.7833 1.01719 11.511L8.60864 4.65838C9.20263 4.12234 10.1188 4.16928 10.6549 4.76327C11.1909 5.35726 11.144 6.27345 10.55 6.80949L4.13374 12.601L10.3616 18.3769C10.9481 18.921 10.9829 19.8378 10.4387 20.4243C10.153 20.732 9.76533 20.8879 9.3759 20.8879Z" fill="#2E3039"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 800 B |
@ -0,0 +1,316 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:device_calendar/device_calendar.dart';
|
||||
import 'package:flutter/widgets.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/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_reminder_view.dart';
|
||||
import 'package:hmg_patient_app_new/services/dialog_service.dart';
|
||||
import 'package:hmg_patient_app_new/services/permission_service.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
import 'package:manage_calendar_events/manage_calendar_events.dart' as ios;
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:timezone/data/latest.dart' as tzl;
|
||||
|
||||
final DeviceCalendarPlugin deviceCalendarPlugin = DeviceCalendarPlugin();
|
||||
final ios.CalendarPlugin _myPlugin = ios.CalendarPlugin();
|
||||
|
||||
class CalendarUtils {
|
||||
static Completer<CalendarUtils>? _completer;
|
||||
|
||||
dynamic get writableCalendars => calendars.firstWhere((c) => !c.isReadOnly!);
|
||||
dynamic calendars;
|
||||
|
||||
CalendarUtils._(this.calendars);
|
||||
|
||||
// static Future<CalendarUtils> getInstance() async {
|
||||
// if (_completer == null) {
|
||||
// _completer = Completer<CalendarUtils>();
|
||||
// print(_completer!.isCompleted);
|
||||
// try {
|
||||
// final dynamic calendarsResult;
|
||||
// if (Platform.isIOS) {
|
||||
// calendarsResult = await _myPlugin.getCalendars();
|
||||
// if (!_completer!.isCompleted) {
|
||||
// _completer?.complete(CalendarUtils._(await calendarsResult!));
|
||||
// }
|
||||
// } else {
|
||||
// calendarsResult = await deviceCalendarPlugin.retrieveCalendars();
|
||||
// if (!_completer!.isCompleted) {
|
||||
// _completer?.complete(CalendarUtils._(await calendarsResult.data!));
|
||||
// }
|
||||
// }
|
||||
// } on Exception catch (e) {
|
||||
// if (!_completer!.isCompleted) {
|
||||
// _completer!.completeError(e);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return _completer!.future;
|
||||
// }
|
||||
|
||||
static Future<CalendarUtils> getInstance() async {
|
||||
tzl.initializeTimeZones();
|
||||
if (_completer != null) {
|
||||
return _completer!.future;
|
||||
}
|
||||
_completer = Completer<CalendarUtils>();
|
||||
try {
|
||||
final dynamic calendarsResult;
|
||||
if (Platform.isIOS) {
|
||||
calendarsResult = await _myPlugin.getCalendars();
|
||||
_completer!.complete(CalendarUtils._(calendarsResult));
|
||||
} else {
|
||||
calendarsResult = await deviceCalendarPlugin.retrieveCalendars();
|
||||
_completer!.complete(CalendarUtils._(calendarsResult.data));
|
||||
}
|
||||
} catch (e) {
|
||||
_completer!.completeError(e);
|
||||
}
|
||||
|
||||
return _completer!.future;
|
||||
}
|
||||
|
||||
Future createOrUpdateEvents({List<DateTime>? scheduleList, String? title, String? description, List<DateTime>? scheduleDateTime, List<DayOfWeek>? daysOfWeek}) async {
|
||||
tzl.initializeTimeZones();
|
||||
List<Event> events = [];
|
||||
Location _currentLocation;
|
||||
if (DateTime.now().timeZoneName == "+04")
|
||||
_currentLocation = getLocation('Asia/Dubai');
|
||||
else
|
||||
_currentLocation = getLocation('Asia/Riyadh');
|
||||
|
||||
scheduleDateTime!.forEach((element) {
|
||||
RecurrenceRule recurrenceRule = RecurrenceRule(
|
||||
// RecurrenceFrequency.Daily,
|
||||
// daysOfWeek: daysOfWeek,
|
||||
// endDate: element,
|
||||
until: element, frequency: Frequency.daily,
|
||||
);
|
||||
//added byAamir Tz Time
|
||||
Event event = Event(writableCalendars!.id,
|
||||
recurrenceRule: recurrenceRule,
|
||||
start: TZDateTime.from(element, _currentLocation),
|
||||
end: TZDateTime.from(element.add(Duration(minutes: 30)), _currentLocation),
|
||||
title: title,
|
||||
description: description);
|
||||
events.add(event);
|
||||
});
|
||||
|
||||
events.forEach((element) {
|
||||
deviceCalendarPlugin.createOrUpdateEvent(element);
|
||||
});
|
||||
}
|
||||
|
||||
Future createOrUpdateEvent({required String title, required String description, required String location, DateTime? scheduleDateTime, String? eventId}) async {
|
||||
RecurrenceRule recurrenceRule = RecurrenceRule(
|
||||
// RecurrenceFrequency.Daily,
|
||||
// daysOfWeek: daysOfWeek,
|
||||
// endDate: scheduleDateTime,
|
||||
until: scheduleDateTime, frequency: Frequency.daily,
|
||||
);
|
||||
|
||||
Location _currentLocation;
|
||||
// if (DateTime.now().timeZoneName == "+04")
|
||||
// _currentLocation = getLocation('Asia/Dubai');
|
||||
// else
|
||||
_currentLocation = getLocation('Asia/Riyadh');
|
||||
|
||||
TZDateTime scheduleDateTimeUTZ = TZDateTime.from(scheduleDateTime!, _currentLocation);
|
||||
|
||||
print("writableCalendars-name: " + writableCalendars.name);
|
||||
print("writableCalendars-Id: " + writableCalendars.id);
|
||||
print("writableCalendarsToString: " + writableCalendars.toString());
|
||||
print("writableCalendarsToString: " + writableCalendars!.id!);
|
||||
Event event = Event(
|
||||
writableCalendars!.id,
|
||||
start: scheduleDateTimeUTZ,
|
||||
end: scheduleDateTimeUTZ.add(Duration(minutes: 30)),
|
||||
title: title,
|
||||
description: description,
|
||||
);
|
||||
|
||||
ios.CalendarEvent iosCalEvent =
|
||||
ios.CalendarEvent(location: location, startDate: scheduleDateTimeUTZ, endDate: scheduleDateTimeUTZ.add(Duration(minutes: 30)), title: title, description: description, isAllDay: false);
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
Result<bool> result = await deviceCalendarPlugin.hasPermissions();
|
||||
print(result);
|
||||
await deviceCalendarPlugin.createOrUpdateEvent(event).catchError((e) {
|
||||
print("catchError " + e.toString());
|
||||
}).whenComplete(() {
|
||||
print("whenComplete Calender ID " + eventId!);
|
||||
});
|
||||
} else {
|
||||
await _myPlugin.createEvent(calendarId: writableCalendars.id!, event: iosCalEvent).catchError((e) {
|
||||
print("catchError " + e.toString());
|
||||
}).whenComplete(() {
|
||||
print("whenComplete Calender ID iOS " + eventId!);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
deleteEvent(String _calendarId, String _eventId) async {
|
||||
if (Platform.isIOS) {
|
||||
await _myPlugin.deleteEvent(calendarId: _calendarId, eventId: _eventId);
|
||||
} else {
|
||||
await deviceCalendarPlugin.deleteEvent(_calendarId, _eventId);
|
||||
}
|
||||
}
|
||||
|
||||
Future retrieveEvents(
|
||||
String calendarId,
|
||||
RetrieveEventsParams retrieveEventsParams,
|
||||
) async {
|
||||
if (Platform.isIOS) {
|
||||
return await _myPlugin.getEvents(calendarId: calendarId);
|
||||
} else {
|
||||
return await deviceCalendarPlugin.retrieveEvents(calendarId, retrieveEventsParams);
|
||||
}
|
||||
}
|
||||
|
||||
Future createCalendar(
|
||||
String calendarName, {
|
||||
Color? calendarColor,
|
||||
String? localAccountName,
|
||||
}) async {
|
||||
return await deviceCalendarPlugin.createCalendar(calendarName, calendarColor: calendarColor, localAccountName: localAccountName);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<Permission, PermissionStatus>> requestPermissions() async {
|
||||
var permissionResults = [Permission.calendarFullAccess].request();
|
||||
return permissionResults;
|
||||
}
|
||||
|
||||
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 {
|
||||
if (Platform.isAndroid) {
|
||||
if (await PermissionService.isCalendarPermissionEnabled()) {
|
||||
_showReminderBottomSheet(context, dateTime, doctorName, eventId, appoDateFormatted, appoTimeFormatted,
|
||||
onSuccess: onSuccess, title: title, description: description, onMultiDateSuccess: onMultiDateSuccess, isMultiAllowed: isMultiAllowed);
|
||||
} else {
|
||||
// Utils.showPermissionConsentDialog(context, TranslationBase.of(context).calendarPermission, () async {
|
||||
// if (await Permission.calendarFullAccess.request().isGranted) {
|
||||
// _showReminderDialog(context, dateTime, doctorName, eventId, appoDateFormatted, appoTimeFormatted,
|
||||
// onSuccess: onSuccess, title: title, description: description, onMultiDateSuccess: onMultiDateSuccess, isMultiAllowed: isMultiAllowed);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
} else {
|
||||
if (await Permission.calendarWriteOnly.request().isGranted) {
|
||||
if (await Permission.calendarFullAccess.request().isGranted) {
|
||||
_showReminderBottomSheet(context, dateTime, doctorName, eventId, appoDateFormatted, appoTimeFormatted,
|
||||
onSuccess: onSuccess, title: title, description: description, onMultiDateSuccess: onMultiDateSuccess, isMultiAllowed: isMultiAllowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showReminderBottomSheet(BuildContext providedContext, DateTime dateTime, String doctorName, String eventId, String appoDateFormatted, String appoTimeFormatted,
|
||||
{required Function onSuccess, String? title, String? description, Function(int)? onMultiDateSuccess, bool? isMultiAllowed}) async {
|
||||
showCommonBottomSheetWithoutHeight(providedContext, title: "Set the timer of reminder".needTranslation, child: PrescriptionReminderView(
|
||||
setReminder: (int value) async {
|
||||
if (!isMultiAllowed!) {
|
||||
if (onMultiDateSuccess == null) {
|
||||
CalendarUtils calendarUtils = await CalendarUtils.getInstance();
|
||||
await calendarUtils.createOrUpdateEvent(
|
||||
title: title ?? "You have appointment with Dr. ".needTranslation + doctorName,
|
||||
description: description ?? "At " + appoDateFormatted + " " + appoTimeFormatted,
|
||||
scheduleDateTime: dateTime,
|
||||
eventId: eventId,
|
||||
location: '');
|
||||
onSuccess();
|
||||
}
|
||||
} else {
|
||||
onMultiDateSuccess!(value);
|
||||
}
|
||||
},
|
||||
), callBackFunc: () {}, isFullScreen: false);
|
||||
}
|
||||
|
||||
setCalender(BuildContext context,
|
||||
{required String eventId, required int selectedMinutes, int? frequencyNumber, required int days, required String orderDate, required String itemDescriptionN, required String route}) async {
|
||||
DateTime actualDate = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, 8, 0);
|
||||
frequencyNumber ??= 2; //Some time frequency number is null so by default will be 2
|
||||
|
||||
int remainingDays = days - (Jiffy.parseFromDateTime(DateTime.now()).diff(Jiffy.parseFromDateTime(DateUtil.convertStringToDate(orderDate)), unit: Unit.day) as int);
|
||||
if (remainingDays.isNegative) {
|
||||
getIt.get<DialogService>().showErrorBottomSheet(message: "Prescription date has been already passed you can not add a reminder for this prescription.");
|
||||
return;
|
||||
}
|
||||
CalendarUtils calendarUtils = await CalendarUtils.getInstance();
|
||||
|
||||
try {
|
||||
for (int i = 0; i < remainingDays; i++) {
|
||||
//event for number of days.
|
||||
for (int j = 0; j < frequencyNumber; j++) {
|
||||
// event for number of times per day.
|
||||
if (j != 0) {
|
||||
actualDate.add(new Duration(hours: 8)); // 8 hours addition for daily dose.
|
||||
}
|
||||
//Time subtraction from actual reminder time. like before 30, or 1 hour.
|
||||
|
||||
actualDate = Jiffy.parseFromDateTime(actualDate).subtract(minutes: selectedMinutes).dateTime;
|
||||
|
||||
calendarUtils.createOrUpdateEvent(
|
||||
title: "$itemDescriptionN} Prescription Reminder",
|
||||
description: "$itemDescriptionN $frequencyNumber $route ",
|
||||
scheduleDateTime: actualDate,
|
||||
eventId: eventId + (i.toString() + j.toString()),
|
||||
location: '', //event id with varitions
|
||||
);
|
||||
print("Creating event #$j for day $i → $actualDate");
|
||||
actualDate = DateTime(actualDate.year, actualDate.month, actualDate.day, 8, 0);
|
||||
}
|
||||
actualDate = Jiffy.parseFromDateTime(actualDate).add(days: 1).dateTime;
|
||||
}
|
||||
} catch (ex) {
|
||||
getIt.get<DialogService>().showErrorBottomSheet(message: "catch:$ex");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkAndRemove(hasReminder, {bool delete = false, String itemDescriptionN = ""}) async {
|
||||
final ios.CalendarPlugin _myPlugin = ios.CalendarPlugin();
|
||||
CalendarUtils calendarUtils = await CalendarUtils.getInstance();
|
||||
DateTime startEventsDate = Jiffy.parseFromDateTime(DateTime.now()).subtract(days: 30).dateTime;
|
||||
DateTime endEventsDate = Jiffy.parseFromDateTime(DateTime.now()).add(days: 120).dateTime;
|
||||
RetrieveEventsParams params = RetrieveEventsParams(startDate: startEventsDate, endDate: endEventsDate);
|
||||
|
||||
if (calendarUtils.calendars != null) {
|
||||
if (Platform.isAndroid) {
|
||||
await processEvents(calendarUtils.calendars, calendarUtils, params, delete, itemDescriptionN, hasReminder);
|
||||
} else {
|
||||
List<ios.Calendar>? iosCalendars = await _myPlugin.getCalendars();
|
||||
if (iosCalendars != null) {
|
||||
await processEvents(iosCalendars.map((cal) => Calendar(id: cal.id, name: cal.name, accountName: cal.accountName)).toList(), calendarUtils, params, delete, itemDescriptionN, hasReminder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> processEvents(List<Calendar> calendars, calendarUtils, params, delete, String itemDescriptionN, hasReminder) async {
|
||||
for (var calendar in calendars) {
|
||||
Result<UnmodifiableListView<Event>> events = await calendarUtils.retrieveEvents(calendar.id!, params);
|
||||
for (var event in events.data!) {
|
||||
if (event.title!.contains(itemDescriptionN)) {
|
||||
if (delete) {
|
||||
await calendarUtils.deleteEvent(calendar, event);
|
||||
// AppToast.showSuccessToast(message: TranslationBase.of(context).reminderCancelSuccess);
|
||||
hasReminder = false;
|
||||
} else {
|
||||
hasReminder = false;
|
||||
// setState(() {
|
||||
// hasReminder = true;
|
||||
// });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:hmg_patient_app_new/features/active_prescriptions/models/active_prescriptions_response_model.dart';
|
||||
import '../../core/api/api_client.dart';
|
||||
import '../../core/api_consts.dart';
|
||||
import '../../core/common_models/generic_api_model.dart';
|
||||
import '../../core/exceptions/api_failure.dart';
|
||||
import '../../services/logger_service.dart';
|
||||
|
||||
abstract class ActivePrescriptionsRepo {
|
||||
|
||||
Future<Either<Failure, GenericApiModel<List<ActivePrescriptionsResponseModel>>>> getActivePrescriptionsDetails();
|
||||
|
||||
}
|
||||
|
||||
class ActivePrescriptionsRepoImp implements ActivePrescriptionsRepo {
|
||||
final ApiClient apiClient;
|
||||
final LoggerService loggerService;
|
||||
|
||||
ActivePrescriptionsRepoImp({required this.loggerService, required this.apiClient});
|
||||
|
||||
@override
|
||||
|
||||
Future<Either<Failure, GenericApiModel<List<ActivePrescriptionsResponseModel>>>> getActivePrescriptionsDetails() async
|
||||
{
|
||||
try {
|
||||
GenericApiModel<List<ActivePrescriptionsResponseModel>>? apiResponse;
|
||||
Failure? failure;
|
||||
await apiClient.post(
|
||||
ApiConsts.getActivePrescriptionsDetails,
|
||||
body: {},
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
var list = response['List_ActiveGetPrescriptionReportByPatientID'];
|
||||
var res = list
|
||||
.map<ActivePrescriptionsResponseModel>(
|
||||
(item) => ActivePrescriptionsResponseModel.fromJson(item))
|
||||
.toList();
|
||||
|
||||
apiResponse = GenericApiModel<List<ActivePrescriptionsResponseModel>>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: null,
|
||||
// data: response,
|
||||
data: res
|
||||
);
|
||||
return apiResponse;
|
||||
} 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()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/features/active_prescriptions/models/active_prescriptions_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/active_prescriptions/active_prescriptions_repo.dart';
|
||||
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
|
||||
|
||||
class ActivePrescriptionsViewModel extends ChangeNotifier {
|
||||
late ActivePrescriptionsRepo activePrescriptionsRepo;
|
||||
late ErrorHandlerService errorHandlerService;
|
||||
List<ActivePrescriptionsResponseModel> activePrescriptionsDetailsList = [];
|
||||
|
||||
ActivePrescriptionsViewModel({
|
||||
required this.activePrescriptionsRepo,
|
||||
required this.errorHandlerService,
|
||||
});
|
||||
|
||||
Future<void> getActiveMedications({
|
||||
Function(dynamic)? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
final result =
|
||||
await activePrescriptionsRepo.getActivePrescriptionsDetails();
|
||||
result.fold(
|
||||
(failure) async =>
|
||||
await errorHandlerService.handleError(failure: failure),
|
||||
(apiResponse) {
|
||||
if (apiResponse.messageStatus == 1) {
|
||||
activePrescriptionsDetailsList = apiResponse.data ?? [];
|
||||
notifyListeners();
|
||||
if (onSuccess != null) onSuccess(apiResponse.data);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
DateTime parseDate(String? date) {
|
||||
if (date == null) return DateTime.now();
|
||||
final regex = RegExp(r"\/Date\((\d+)([+-]\d+)?\)\/");
|
||||
final match = regex.firstMatch(date);
|
||||
if (match != null) {
|
||||
final millis = int.parse(match.group(1)!);
|
||||
return DateTime.fromMillisecondsSinceEpoch(millis);
|
||||
}
|
||||
return DateTime.tryParse(date) ?? DateTime.now();
|
||||
}
|
||||
|
||||
List<DateTime> generateMedicationDays(ActivePrescriptionsResponseModel med) {
|
||||
final start = parseDate(med.startDate);
|
||||
final duration = med.days ?? 0;
|
||||
if (duration <= 0) return [];
|
||||
final f = (med.frequency ?? "").toLowerCase().trim();
|
||||
int intervalDays = 1;
|
||||
|
||||
if (f.contains("every six hours") ||
|
||||
f.contains("every 6 hours") ||
|
||||
f.contains("every four hours") ||
|
||||
f.contains("every 4 hours") ||
|
||||
f.contains("every eight hours") ||
|
||||
f.contains("every 8 hours") ||
|
||||
f.contains("every 12 hours") ||
|
||||
f.contains("every twelve hours") ||
|
||||
f.contains("every 24 hours") ||
|
||||
f.contains("3 times a day") ||
|
||||
f.contains("once a day")) {
|
||||
intervalDays = 1;
|
||||
}
|
||||
else if (f.contains("once a week")) {
|
||||
intervalDays = 7;
|
||||
}
|
||||
else if (f.contains("every 3 days")) {
|
||||
intervalDays = 3;
|
||||
}
|
||||
else if (f.contains("every other day")) {
|
||||
intervalDays = 2;
|
||||
}
|
||||
|
||||
List<DateTime> result = [];
|
||||
for (int offset = 0; offset < duration; offset += intervalDays) {
|
||||
result.add(start.add(Duration(days: offset)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool sameYMD(DateTime a, DateTime b) =>
|
||||
a.year == b.year && a.month == b.month && a.day == b.day;
|
||||
|
||||
List<ActivePrescriptionsResponseModel> getMedsForSelectedDay(
|
||||
DateTime selectedDate) {
|
||||
final clean = DateTime(selectedDate.year, selectedDate.month, selectedDate.day);
|
||||
|
||||
return activePrescriptionsDetailsList.where((med) {
|
||||
final days = generateMedicationDays(med);
|
||||
return days.any((d) => sameYMD(d, clean));
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
|
||||
class ActivePrescriptionsResponseModel {
|
||||
String? itemId;
|
||||
String? itemDescription;
|
||||
String? route;
|
||||
String? frequency;
|
||||
int? frequencyNumber;
|
||||
int? doseDailyQuantity;
|
||||
int? days;
|
||||
String? startDate;
|
||||
String? endDate;
|
||||
String? orderDate;
|
||||
String? productImageString;
|
||||
bool isReminderOn;
|
||||
List<String?> selectedDoseTimes = [];
|
||||
|
||||
ActivePrescriptionsResponseModel({
|
||||
this.itemId,
|
||||
this.itemDescription,
|
||||
this.route,
|
||||
this.frequency,
|
||||
this.frequencyNumber,
|
||||
this.doseDailyQuantity,
|
||||
this.days,
|
||||
this.startDate,
|
||||
this.endDate,
|
||||
this.orderDate,
|
||||
this.productImageString,
|
||||
this.isReminderOn = false,
|
||||
List<String?>? selectedDoseTimes,
|
||||
}) {
|
||||
this.selectedDoseTimes = selectedDoseTimes ?? [];
|
||||
}
|
||||
|
||||
/// ========== JSON FROM ==========
|
||||
factory ActivePrescriptionsResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
return ActivePrescriptionsResponseModel(
|
||||
itemId: json["ItemID"]?.toString() ?? "",
|
||||
itemDescription: json["ItemDescription"] ?? "",
|
||||
route: json["Route"] ?? "",
|
||||
frequency: json["Frequency"] ?? "",
|
||||
frequencyNumber: json["FrequencyNumber"],
|
||||
doseDailyQuantity: json["DoseDailyQuantity"] ?? 1,
|
||||
days: json["Days"] ?? 0,
|
||||
startDate: json["StartDate"] ?? "",
|
||||
endDate: json["EndDate"] ?? "",
|
||||
orderDate: json["OrderDate"] ?? "",
|
||||
productImageString: json["ProductImageString"] ?? "",
|
||||
isReminderOn: json["IsReminderOn"] == true,
|
||||
selectedDoseTimes:
|
||||
(json["SelectedDoseTimes"] as List<dynamic>?)
|
||||
?.map((e) => e?.toString())
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// ========== JSON TO ==========
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
"ItemID": itemId,
|
||||
"ItemDescription": itemDescription,
|
||||
"Route": route,
|
||||
"Frequency": frequency,
|
||||
"FrequencyNumber": frequencyNumber,
|
||||
"DoseDailyQuantity": doseDailyQuantity,
|
||||
"Days": days,
|
||||
"StartDate": startDate,
|
||||
"EndDate": endDate,
|
||||
"OrderDate": orderDate,
|
||||
"ProductImageString": productImageString,
|
||||
"IsReminderOn": isReminderOn,
|
||||
"SelectedDoseTimes": selectedDoseTimes,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../core/api/api_client.dart';
|
||||
import '../../core/api_consts.dart';
|
||||
import '../../core/common_models/generic_api_model.dart';
|
||||
import '../../core/exceptions/api_failure.dart';
|
||||
import '../../services/logger_service.dart';
|
||||
|
||||
abstract class MonthlyReportsRepo {
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> saveMonthlyReport({
|
||||
String? email,
|
||||
});
|
||||
}
|
||||
|
||||
class MonthlyReportsRepoImp implements MonthlyReportsRepo {
|
||||
final ApiClient apiClient;
|
||||
final LoggerService loggerService;
|
||||
|
||||
MonthlyReportsRepoImp({
|
||||
required this.loggerService,
|
||||
required this.apiClient,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> saveMonthlyReport({
|
||||
String? email,
|
||||
}) async {
|
||||
try {
|
||||
Failure? failure;
|
||||
|
||||
GenericApiModel<dynamic>? reportApiResponse;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.getMonthlyReports,
|
||||
body: <String, dynamic>{},
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
reportApiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: response,
|
||||
);
|
||||
} catch (e) {
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (reportApiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
|
||||
if ((reportApiResponse!.messageStatus ?? 0) != 1) {
|
||||
return Right(reportApiResponse!);
|
||||
}
|
||||
|
||||
GenericApiModel<dynamic>? emailApiResponse;
|
||||
|
||||
final Map<String, dynamic> emailRequest = <String, dynamic>{};
|
||||
|
||||
if (email != null && email.trim().isNotEmpty) {
|
||||
emailRequest["Email"] = email.trim();
|
||||
}
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.updatePatientEmail,
|
||||
body: emailRequest,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
emailApiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: response,
|
||||
);
|
||||
} catch (e) {
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (emailApiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
|
||||
return Right(emailApiResponse!);
|
||||
} catch (e) {
|
||||
loggerService.logError("MonthlyReportsRepo.saveMonthlyReport error: $e");
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
|
||||
import 'monthly_reports_repo.dart';
|
||||
class MonthlyReportsViewModel extends ChangeNotifier {
|
||||
final MonthlyReportsRepo monthlyReportsRepo;
|
||||
final ErrorHandlerService errorHandlerService;
|
||||
|
||||
bool isLoading = false;
|
||||
|
||||
MonthlyReportsViewModel({
|
||||
required this.monthlyReportsRepo,
|
||||
required this.errorHandlerService,
|
||||
});
|
||||
|
||||
Future<bool> saveMonthlyReport({String? email}) async {
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await monthlyReportsRepo.saveMonthlyReport(email: email);
|
||||
|
||||
final success = result.fold(
|
||||
(failure) {
|
||||
errorHandlerService.handleError(failure: failure);
|
||||
return false;
|
||||
},
|
||||
(apiResponse) => (apiResponse.messageStatus ?? 0) == 1,
|
||||
);
|
||||
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
return success;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../core/api/api_client.dart';
|
||||
import '../../core/api_consts.dart';
|
||||
import '../../core/exceptions/api_failure.dart';
|
||||
import '../../services/logger_service.dart';
|
||||
|
||||
abstract class TermsConditionsRepo {
|
||||
Future<Either<Failure, String>> getTermsConditions();
|
||||
}
|
||||
|
||||
class TermsConditionsRepoImp implements TermsConditionsRepo {
|
||||
final ApiClient apiClient;
|
||||
final LoggerService loggerService;
|
||||
|
||||
TermsConditionsRepoImp({
|
||||
required this.loggerService,
|
||||
required this.apiClient,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, String>> getTermsConditions() async {
|
||||
Failure? failure;
|
||||
String? html;
|
||||
|
||||
try {
|
||||
await apiClient.post(
|
||||
ApiConsts.getTermsConditions,
|
||||
body: <String, dynamic>{},
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType ?? ServerFailure(error.toString());
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
|
||||
final content = response['UserAgreementContent'];
|
||||
|
||||
if (content is String && content.isNotEmpty) {
|
||||
html = content;
|
||||
} else {
|
||||
failure = DataParsingFailure(
|
||||
'UserAgreementContent is null or not String');
|
||||
}
|
||||
} catch (e) {
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
failure = UnknownFailure(e.toString());
|
||||
}
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (html == null || html!.isEmpty) {
|
||||
return Left(ServerFailure('No terms and conditions returned'));
|
||||
}
|
||||
|
||||
return Right(html!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/features/monthly_reports/terms_conditions_repo.dart';
|
||||
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
|
||||
|
||||
class TermsConditionsViewModel extends ChangeNotifier {
|
||||
final TermsConditionsRepo termsConditionsRepo;
|
||||
final ErrorHandlerService errorHandlerService;
|
||||
|
||||
String? termsConditionsHtml;
|
||||
bool isLoading = false;
|
||||
|
||||
TermsConditionsViewModel({
|
||||
required this.termsConditionsRepo,
|
||||
required this.errorHandlerService,
|
||||
});
|
||||
|
||||
Future<void> getTermsConditions({
|
||||
Function()? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await termsConditionsRepo.getTermsConditions();
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
await errorHandlerService.handleError(failure: failure);
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
if (onError != null) {
|
||||
onError(failure.message ?? 'Something went wrong');
|
||||
}
|
||||
},
|
||||
(html) {
|
||||
termsConditionsHtml = html;
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
if (onSuccess != null) onSuccess();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,96 @@
|
||||
class NotificationResponseModel {
|
||||
int? id;
|
||||
int? recordId;
|
||||
int? patientID;
|
||||
bool? projectOutSA;
|
||||
String? deviceType;
|
||||
String? deviceToken;
|
||||
String? message;
|
||||
String? messageType;
|
||||
String? messageTypeData;
|
||||
String? videoURL;
|
||||
bool? isQueue;
|
||||
String? isQueueOn;
|
||||
String? createdOn;
|
||||
dynamic createdBy;
|
||||
String? notificationType;
|
||||
bool? isSent;
|
||||
String? isSentOn;
|
||||
bool? isRead;
|
||||
dynamic isReadOn;
|
||||
int? channelID;
|
||||
int? projectID;
|
||||
|
||||
NotificationResponseModel(
|
||||
{this.id,
|
||||
this.recordId,
|
||||
this.patientID,
|
||||
this.projectOutSA,
|
||||
this.deviceType,
|
||||
this.deviceToken,
|
||||
this.message,
|
||||
this.messageType,
|
||||
this.messageTypeData,
|
||||
this.videoURL,
|
||||
this.isQueue,
|
||||
this.isQueueOn,
|
||||
this.createdOn,
|
||||
this.createdBy,
|
||||
this.notificationType,
|
||||
this.isSent,
|
||||
this.isSentOn,
|
||||
this.isRead,
|
||||
this.isReadOn,
|
||||
this.channelID,
|
||||
this.projectID});
|
||||
|
||||
NotificationResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
id = json['Id'];
|
||||
recordId = json['RecordId'];
|
||||
patientID = json['PatientID'];
|
||||
projectOutSA = json['ProjectOutSA'];
|
||||
deviceType = json['DeviceType'];
|
||||
deviceToken = json['DeviceToken'];
|
||||
message = json['Message'];
|
||||
messageType = json['MessageType'];
|
||||
messageTypeData = json['MessageTypeData'];
|
||||
videoURL = json['VideoURL'];
|
||||
isQueue = json['IsQueue'];
|
||||
isQueueOn = json['IsQueueOn'];
|
||||
createdOn = json['CreatedOn'];
|
||||
createdBy = json['CreatedBy'];
|
||||
notificationType = json['NotificationType'];
|
||||
isSent = json['IsSent'];
|
||||
isSentOn = json['IsSentOn'];
|
||||
isRead = json['IsRead'];
|
||||
isReadOn = json['IsReadOn'];
|
||||
channelID = json['ChannelID'];
|
||||
projectID = json['ProjectID'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['Id'] = this.id;
|
||||
data['RecordId'] = this.recordId;
|
||||
data['PatientID'] = this.patientID;
|
||||
data['ProjectOutSA'] = this.projectOutSA;
|
||||
data['DeviceType'] = this.deviceType;
|
||||
data['DeviceToken'] = this.deviceToken;
|
||||
data['Message'] = this.message;
|
||||
data['MessageType'] = this.messageType;
|
||||
data['MessageTypeData'] = this.messageTypeData;
|
||||
data['VideoURL'] = this.videoURL;
|
||||
data['IsQueue'] = this.isQueue;
|
||||
data['IsQueueOn'] = this.isQueueOn;
|
||||
data['CreatedOn'] = this.createdOn;
|
||||
data['CreatedBy'] = this.createdBy;
|
||||
data['NotificationType'] = this.notificationType;
|
||||
data['IsSent'] = this.isSent;
|
||||
data['IsSentOn'] = this.isSentOn;
|
||||
data['IsRead'] = this.isRead;
|
||||
data['IsReadOn'] = this.isReadOn;
|
||||
data['ChannelID'] = this.channelID;
|
||||
data['ProjectID'] = this.projectID;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
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/features/notifications/models/resp_models/notification_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/services/logger_service.dart';
|
||||
|
||||
abstract class NotificationsRepo {
|
||||
Future<Either<Failure, GenericApiModel<List<NotificationResponseModel>>>> getAllNotifications({
|
||||
required int notificationStatusID,
|
||||
required int pagingSize,
|
||||
required int currentPage,
|
||||
});
|
||||
}
|
||||
|
||||
class NotificationsRepoImp implements NotificationsRepo {
|
||||
final ApiClient apiClient;
|
||||
final LoggerService loggerService;
|
||||
|
||||
NotificationsRepoImp({required this.loggerService, required this.apiClient});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<List<NotificationResponseModel>>>> getAllNotifications({
|
||||
required int notificationStatusID,
|
||||
required int pagingSize,
|
||||
required int currentPage,
|
||||
}) async {
|
||||
Map<String, dynamic> mapDevice = {
|
||||
"NotificationStatusID": notificationStatusID,
|
||||
"pagingSize": pagingSize,
|
||||
"currentPage": currentPage,
|
||||
};
|
||||
|
||||
try {
|
||||
GenericApiModel<List<NotificationResponseModel>>? apiResponse;
|
||||
Failure? failure;
|
||||
await apiClient.post(
|
||||
GET_ALL_NOTIFICATIONS,
|
||||
body: mapDevice,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
final list = response['List_GetAllNotificationsFromPool'];
|
||||
if (list == null || list.isEmpty) {
|
||||
// Return empty list if no notifications
|
||||
apiResponse = GenericApiModel<List<NotificationResponseModel>>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: null,
|
||||
data: [],
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final notifications = list.map((item) => NotificationResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<NotificationResponseModel>();
|
||||
|
||||
apiResponse = GenericApiModel<List<NotificationResponseModel>>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: null,
|
||||
data: notifications,
|
||||
);
|
||||
} 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/features/notifications/models/resp_models/notification_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/notifications/notifications_repo.dart';
|
||||
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
|
||||
|
||||
class NotificationsViewModel extends ChangeNotifier {
|
||||
bool isNotificationsLoading = false;
|
||||
bool hasMoreNotifications = true;
|
||||
|
||||
NotificationsRepo notificationsRepo;
|
||||
ErrorHandlerService errorHandlerService;
|
||||
|
||||
List<NotificationResponseModel> notificationsList = [];
|
||||
|
||||
int currentPage = 0;
|
||||
int pagingSize = 14;
|
||||
int notificationStatusID = 2; // Default to status 2 (e.g., unread)
|
||||
|
||||
NotificationsViewModel({
|
||||
required this.notificationsRepo,
|
||||
required this.errorHandlerService,
|
||||
});
|
||||
|
||||
initNotificationsViewModel({int? statusID}) {
|
||||
notificationsList.clear();
|
||||
currentPage = 0;
|
||||
hasMoreNotifications = true;
|
||||
isNotificationsLoading = true;
|
||||
// if (statusID != null) {
|
||||
// notificationStatusID = statusID;
|
||||
// }
|
||||
// getAllNotifications();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
setNotificationStatusID(int statusID) {
|
||||
notificationStatusID = statusID;
|
||||
notificationsList.clear();
|
||||
currentPage = 0;
|
||||
hasMoreNotifications = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getAllNotifications({
|
||||
Function(dynamic)? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
isNotificationsLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await notificationsRepo.getAllNotifications(
|
||||
notificationStatusID: notificationStatusID,
|
||||
pagingSize: pagingSize,
|
||||
currentPage: currentPage,
|
||||
);
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isNotificationsLoading = false;
|
||||
notifyListeners();
|
||||
await errorHandlerService.handleError(failure: failure);
|
||||
if (onError != null) {
|
||||
onError(failure.toString());
|
||||
}
|
||||
},
|
||||
(apiResponse) {
|
||||
isNotificationsLoading = false;
|
||||
if (apiResponse.messageStatus == 2) {
|
||||
notifyListeners();
|
||||
if (onError != null) {
|
||||
onError(apiResponse.errorMessage ?? "Unknown error");
|
||||
}
|
||||
} else if (apiResponse.messageStatus == 1) {
|
||||
final newNotifications = apiResponse.data ?? [];
|
||||
|
||||
if (newNotifications.isEmpty || newNotifications.length < pagingSize) {
|
||||
hasMoreNotifications = false;
|
||||
}
|
||||
|
||||
notificationsList.addAll(newNotifications);
|
||||
currentPage++;
|
||||
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> refreshNotifications({
|
||||
Function(dynamic)? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
notificationsList.clear();
|
||||
currentPage = 0;
|
||||
hasMoreNotifications = true;
|
||||
await getAllNotifications(onSuccess: onSuccess, onError: onError);
|
||||
}
|
||||
|
||||
Future<void> loadMoreNotifications({
|
||||
Function(dynamic)? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
await getAllNotifications(onSuccess: onSuccess, onError: onError);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,183 @@
|
||||
|
||||
|
||||
class QrParkingResponseModel {
|
||||
dynamic totalRecords;
|
||||
dynamic nRowID;
|
||||
int? qRParkingID;
|
||||
String? description;
|
||||
String? descriptionN;
|
||||
dynamic qRCompare;
|
||||
dynamic qRValue;
|
||||
String? imagePath;
|
||||
bool? isActive;
|
||||
int? parkingID;
|
||||
int? branchID;
|
||||
int? companyID;
|
||||
int? buildingID;
|
||||
int? rowID;
|
||||
int? gateID;
|
||||
int? floorID;
|
||||
dynamic imagePath1;
|
||||
int? createdBy;
|
||||
String? createdOn;
|
||||
dynamic editedBy;
|
||||
dynamic editedOn;
|
||||
String? parkingDescription;
|
||||
String? parkingDescriptionN;
|
||||
String? gateDescription;
|
||||
String? gateDescriptionN;
|
||||
String? branchDescription;
|
||||
String? branchDescriptionN;
|
||||
String? companyDescription;
|
||||
String? companyDescriptionN;
|
||||
String? rowDescription;
|
||||
String? rowDescriptionN;
|
||||
String? floorDescription;
|
||||
String? floorDescriptionN;
|
||||
String? buildingDescription;
|
||||
String? buildingDescriptionN;
|
||||
String? qRParkingCode;
|
||||
String? parkingCode;
|
||||
double? latitude;
|
||||
double? longitude;
|
||||
String? qRImageStr;
|
||||
|
||||
QrParkingResponseModel({
|
||||
this.totalRecords,
|
||||
this.nRowID,
|
||||
this.qRParkingID,
|
||||
this.description,
|
||||
this.descriptionN,
|
||||
this.qRCompare,
|
||||
this.qRValue,
|
||||
this.imagePath,
|
||||
this.isActive,
|
||||
this.parkingID,
|
||||
this.branchID,
|
||||
this.companyID,
|
||||
this.buildingID,
|
||||
this.rowID,
|
||||
this.gateID,
|
||||
this.floorID,
|
||||
this.imagePath1,
|
||||
this.createdBy,
|
||||
this.createdOn,
|
||||
this.editedBy,
|
||||
this.editedOn,
|
||||
this.parkingDescription,
|
||||
this.parkingDescriptionN,
|
||||
this.gateDescription,
|
||||
this.gateDescriptionN,
|
||||
this.branchDescription,
|
||||
this.branchDescriptionN,
|
||||
this.companyDescription,
|
||||
this.companyDescriptionN,
|
||||
this.rowDescription,
|
||||
this.rowDescriptionN,
|
||||
this.floorDescription,
|
||||
this.floorDescriptionN,
|
||||
this.buildingDescription,
|
||||
this.buildingDescriptionN,
|
||||
this.qRParkingCode,
|
||||
this.parkingCode,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
this.qRImageStr,
|
||||
});
|
||||
|
||||
QrParkingResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
totalRecords = json['TotalRecords'];
|
||||
nRowID = json['nRowID'];
|
||||
qRParkingID = json['QRParkingID'];
|
||||
description = json['Description'];
|
||||
descriptionN = json['DescriptionN'];
|
||||
qRCompare = json['QRCompare'];
|
||||
qRValue = json['QRValue'];
|
||||
imagePath = json['ImagePath'];
|
||||
isActive = json['IsActive'];
|
||||
parkingID = json['ParkingID'];
|
||||
branchID = json['BranchID'];
|
||||
companyID = json['CompanyID'];
|
||||
buildingID = json['BuildingID'];
|
||||
rowID = json['RowID'];
|
||||
gateID = json['GateID'];
|
||||
floorID = json['FloorID'];
|
||||
imagePath1 = json['ImagePath1'];
|
||||
createdBy = json['CreatedBy'];
|
||||
createdOn = json['CreatedOn'];
|
||||
editedBy = json['EditedBy'];
|
||||
editedOn = json['EditedOn'];
|
||||
parkingDescription = json['ParkingDescription'];
|
||||
parkingDescriptionN = json['ParkingDescriptionN'];
|
||||
gateDescription = json['GateDescription'];
|
||||
gateDescriptionN = json['GateDescriptionN'];
|
||||
branchDescription = json['BranchDescription'];
|
||||
branchDescriptionN = json['BranchDescriptionN'];
|
||||
companyDescription = json['CompanyDescription'];
|
||||
companyDescriptionN = json['CompanyDescriptionN'];
|
||||
rowDescription = json['RowDescription'];
|
||||
rowDescriptionN = json['RowDescriptionN'];
|
||||
floorDescription = json['FloorDescription'];
|
||||
floorDescriptionN = json['FloorDescriptionN'];
|
||||
buildingDescription = json['BuildingDescription'];
|
||||
buildingDescriptionN = json['BuildingDescriptionN'];
|
||||
qRParkingCode = json['QRParkingCode'];
|
||||
parkingCode = json['ParkingCode'];
|
||||
latitude = _toDouble(json['Latitude']);
|
||||
longitude = _toDouble(json['Longitude']);
|
||||
qRImageStr = json['QRImageStr'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['TotalRecords'] = totalRecords;
|
||||
data['nRowID'] = nRowID;
|
||||
data['QRParkingID'] = qRParkingID;
|
||||
data['Description'] = description;
|
||||
data['DescriptionN'] = descriptionN;
|
||||
data['QRCompare'] = qRCompare;
|
||||
data['QRValue'] = qRValue;
|
||||
data['ImagePath'] = imagePath;
|
||||
data['IsActive'] = isActive;
|
||||
data['ParkingID'] = parkingID;
|
||||
data['BranchID'] = branchID;
|
||||
data['CompanyID'] = companyID;
|
||||
data['BuildingID'] = buildingID;
|
||||
data['RowID'] = rowID;
|
||||
data['GateID'] = gateID;
|
||||
data['FloorID'] = floorID;
|
||||
data['ImagePath1'] = imagePath1;
|
||||
data['CreatedBy'] = createdBy;
|
||||
data['CreatedOn'] = createdOn;
|
||||
data['EditedBy'] = editedBy;
|
||||
data['EditedOn'] = editedOn;
|
||||
data['ParkingDescription'] = parkingDescription;
|
||||
data['ParkingDescriptionN'] = parkingDescriptionN;
|
||||
data['GateDescription'] = gateDescription;
|
||||
data['GateDescriptionN'] = gateDescriptionN;
|
||||
data['BranchDescription'] = branchDescription;
|
||||
data['BranchDescriptionN'] = branchDescriptionN;
|
||||
data['CompanyDescription'] = companyDescription;
|
||||
data['CompanyDescriptionN'] = companyDescriptionN;
|
||||
data['RowDescription'] = rowDescription;
|
||||
data['RowDescriptionN'] = rowDescriptionN;
|
||||
data['FloorDescription'] = floorDescription;
|
||||
data['FloorDescriptionN'] = floorDescriptionN;
|
||||
data['BuildingDescription'] = buildingDescription;
|
||||
data['BuildingDescriptionN'] = buildingDescriptionN;
|
||||
data['QRParkingCode'] = qRParkingCode;
|
||||
data['ParkingCode'] = parkingCode;
|
||||
data['Latitude'] = latitude;
|
||||
data['Longitude'] = longitude;
|
||||
data['QRImageStr'] = qRImageStr;
|
||||
return data;
|
||||
}
|
||||
|
||||
static double? _toDouble(dynamic v) {
|
||||
if (v == null) return null;
|
||||
if (v is double) return v;
|
||||
if (v is int) return v.toDouble();
|
||||
return double.tryParse(v.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:hmg_patient_app_new/features/qr_parking/models/qr_parking_response_model.dart';
|
||||
import '../../core/api/api_client.dart';
|
||||
import '../../core/api_consts.dart';
|
||||
import '../../core/common_models/generic_api_model.dart';
|
||||
import '../../core/exceptions/api_failure.dart';
|
||||
import '../../services/logger_service.dart';
|
||||
|
||||
|
||||
abstract class QrParkingRepo {
|
||||
Future<Either<Failure, GenericApiModel<List<QrParkingResponseModel>>>>
|
||||
getQrParking({
|
||||
required int qrParkingId,
|
||||
});
|
||||
}
|
||||
|
||||
class QrParkingRepoImp implements QrParkingRepo {
|
||||
final ApiClient apiClient;
|
||||
final LoggerService loggerService;
|
||||
|
||||
QrParkingRepoImp({
|
||||
required this.loggerService,
|
||||
required this.apiClient,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<List<QrParkingResponseModel>>>>
|
||||
getQrParking({required int qrParkingId}) async {
|
||||
try {
|
||||
GenericApiModel<List<QrParkingResponseModel>>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.getQrParkingDetails, // GetQRParkingByID
|
||||
body: {'QRParkingID': qrParkingId},
|
||||
onFailure: (error, statusCode,
|
||||
{messageStatus, failureType}) {
|
||||
failure = failureType ??
|
||||
StatusCodeFailure("$error ($statusCode)");
|
||||
},
|
||||
onSuccess: (response, statusCode,
|
||||
{messageStatus, errorMessage}) {
|
||||
final list =
|
||||
(response['List_SWP_QRParkingModel'] as List?) ?? [];
|
||||
|
||||
final res = list
|
||||
.map((e) => QrParkingResponseModel.fromJson(
|
||||
Map<String, dynamic>.from(e),
|
||||
))
|
||||
.toList();
|
||||
|
||||
apiResponse = GenericApiModel<List<QrParkingResponseModel>>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: null,
|
||||
data: res,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) {
|
||||
return Left(ServerFailure("Unknown error"));
|
||||
}
|
||||
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,144 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:barcode_scan2/barcode_scan2.dart';
|
||||
import 'package:hmg_patient_app_new/features/qr_parking/qr_parking_repo.dart';
|
||||
|
||||
import '../../services/cache_service.dart';
|
||||
import '../../services/error_handler_service.dart';
|
||||
import 'models/qr_parking_response_model.dart';
|
||||
|
||||
|
||||
class QrParkingViewModel extends ChangeNotifier {
|
||||
final QrParkingRepo qrParkingRepo;
|
||||
final ErrorHandlerService errorHandlerService;
|
||||
final CacheService cacheService;
|
||||
String IS_GO_TO_PARKING = 'IS_GO_TO_PARKING';
|
||||
|
||||
bool isLoading = false;
|
||||
String? error;
|
||||
|
||||
bool isSavePark = false;
|
||||
QrParkingResponseModel? qrParkingModel;
|
||||
List<QrParkingResponseModel> qrParkingList = [];
|
||||
|
||||
QrParkingViewModel({
|
||||
required this.qrParkingRepo,
|
||||
required this.errorHandlerService,
|
||||
required this.cacheService,
|
||||
});
|
||||
|
||||
|
||||
Future<QrParkingResponseModel?> scanAndGetParking() async {
|
||||
try {
|
||||
error = null;
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await BarcodeScanner.scan();
|
||||
|
||||
if (result.type != ResultType.Barcode) {
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
return null;
|
||||
}
|
||||
|
||||
final raw = result.rawContent.trim();
|
||||
if (raw.isEmpty) {
|
||||
error = "Invalid QR Code";
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
return null;
|
||||
}
|
||||
|
||||
final qrParkingId = _extractQrParkingId(raw);
|
||||
if (qrParkingId == null) {
|
||||
error = "Invalid QR Code";
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
return null;
|
||||
}
|
||||
|
||||
final apiResult =
|
||||
await qrParkingRepo.getQrParking(qrParkingId: qrParkingId);
|
||||
|
||||
final model = apiResult.fold(
|
||||
(failure) {
|
||||
errorHandlerService.handleError(failure: failure);
|
||||
error = failure.toString();
|
||||
return null;
|
||||
},
|
||||
(apiResponse) {
|
||||
qrParkingList = apiResponse.data ?? [];
|
||||
if (qrParkingList.isNotEmpty) {
|
||||
return qrParkingList.first;
|
||||
}
|
||||
error = "Invalid Qr Code";
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
if (model != null) {
|
||||
qrParkingModel = model;
|
||||
isSavePark = true;
|
||||
|
||||
await cacheService.saveObject(
|
||||
key: IS_GO_TO_PARKING,
|
||||
value: model.toJson(),
|
||||
);
|
||||
}
|
||||
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
return model;
|
||||
} catch (e) {
|
||||
error = "Scan error";
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Load saved parking
|
||||
Future<void> getIsSaveParking() async {
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final parking =
|
||||
await cacheService.getObject(key: IS_GO_TO_PARKING);
|
||||
|
||||
if (parking != null) {
|
||||
isSavePark = true;
|
||||
qrParkingModel = QrParkingResponseModel.fromJson(
|
||||
Map<String, dynamic>.from(parking),
|
||||
);
|
||||
} else {
|
||||
isSavePark = false;
|
||||
qrParkingModel = null;
|
||||
}
|
||||
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Reset parking
|
||||
Future<void> clearParking() async {
|
||||
await cacheService.remove(key: IS_GO_TO_PARKING);
|
||||
isSavePark = false;
|
||||
qrParkingModel = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
int? _extractQrParkingId(String raw) {
|
||||
try {
|
||||
if (raw.startsWith("{")) {
|
||||
final data = jsonDecode(raw);
|
||||
return int.tryParse(data['QRParkingID'].toString());
|
||||
}
|
||||
return int.tryParse(raw);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue