Merge branch 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New into dev_sultan
* 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New: fixed parking qr added parking part added monthly report added monthly report added monthly report fix error monthly report fix toggle fix toggle active medication active medication active medicationpull/154/head
commit
0ddaa6899d
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
@ -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,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
@ -0,0 +1,310 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.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/monthly_reports/monthly_reports_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/monthly_reports/user_agreement_page.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../generated/locale_keys.g.dart';
|
||||
import '../../theme/colors.dart';
|
||||
import '../../widgets/appbar/app_bar_widget.dart';
|
||||
import '../../widgets/input_widget.dart';
|
||||
import '../../widgets/loader/bottomsheet_loader.dart';
|
||||
|
||||
class MonthlyReportsPage extends StatefulWidget {
|
||||
const MonthlyReportsPage({super.key});
|
||||
|
||||
@override
|
||||
State<MonthlyReportsPage> createState() => _MonthlyReportsPageState();
|
||||
}
|
||||
|
||||
class _MonthlyReportsPageState extends State<MonthlyReportsPage> {
|
||||
bool isHealthSummaryEnabled = false;
|
||||
bool isTermsAccepted = false;
|
||||
|
||||
final TextEditingController emailController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
emailController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _showError(String message) {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showSuccessSnackBar() {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
"Successfully updated".needTranslation,
|
||||
style: const TextStyle(
|
||||
color: AppColors.whiteColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
backgroundColor: AppColors.textGreenColor,
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onSavePressed() async {
|
||||
if (!isTermsAccepted) {
|
||||
_showError("Please accept the terms and conditions".needTranslation);
|
||||
return;
|
||||
}
|
||||
|
||||
final email = emailController.text.trim();
|
||||
if (email.isEmpty) {
|
||||
_showError("Please enter your email".needTranslation);
|
||||
return;
|
||||
}
|
||||
|
||||
final vm = context.read<MonthlyReportsViewModel>();
|
||||
|
||||
// LoaderBottomSheet.showLoader();
|
||||
final ok = await vm.saveMonthlyReport(email: email);
|
||||
// LoaderBottomSheet.hideLoader();
|
||||
|
||||
if (ok) {
|
||||
setState(() => isHealthSummaryEnabled = true);
|
||||
_showSuccessSnackBar();
|
||||
} else {
|
||||
// _showError("Failed to update".needTranslation);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.scaffoldBgColor,
|
||||
appBar: CustomAppBar(
|
||||
onBackPressed: () => Navigator.of(context).pop(),
|
||||
onLanguageChanged: (_) {},
|
||||
hideLogoAndLang: true,
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Monthly Reports".needTranslation,
|
||||
style: TextStyle(
|
||||
color: AppColors.textColor,
|
||||
fontSize: 27.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
|
||||
height: 54.h,
|
||||
alignment: Alignment.center,
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: (12.r),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Patient Health Summary Report".needTranslation,
|
||||
style: TextStyle(
|
||||
color: AppColors.textColor,
|
||||
fontSize: 14.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
_buildToggle(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
TextInputWidget(
|
||||
controller: emailController,
|
||||
labelText: "Eamil*".needTranslation,
|
||||
hintText: "email@email.com",
|
||||
isEnable: true,
|
||||
prefix: null,
|
||||
isAllowRadius: true,
|
||||
isBorderAllowed: false,
|
||||
isAllowLeadingIcon: true,
|
||||
autoFocus: true,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
|
||||
onChange: (value) {
|
||||
setState(() {});
|
||||
},
|
||||
).paddingOnly(top: 8.h, bottom: 8.h),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
"To View The Terms and Conditions".needTranslation,
|
||||
style: TextStyle(
|
||||
color: AppColors.textColor,
|
||||
fontSize: 14.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
child: Text(
|
||||
"Click here".needTranslation,
|
||||
style: TextStyle(
|
||||
color: AppColors.primaryRedColor,
|
||||
fontSize: 14.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const UserAgreementPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 12.h),
|
||||
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => isTermsAccepted = !isTermsAccepted),
|
||||
child: Row(
|
||||
children: [
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
height: 24.h,
|
||||
width: 24.h,
|
||||
decoration: BoxDecoration(
|
||||
color: isTermsAccepted
|
||||
? AppColors.textGreenColor
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(
|
||||
color: isTermsAccepted
|
||||
? AppColors.lightGreenColor
|
||||
: AppColors.greyColor,
|
||||
width: 2.h,
|
||||
),
|
||||
),
|
||||
child: isTermsAccepted
|
||||
? Icon(Icons.check, size: 16.f, color: AppColors.whiteColor,)
|
||||
: null,
|
||||
),
|
||||
SizedBox(width: 12.h),
|
||||
Text(
|
||||
"I agree to the terms and conditions".needTranslation,
|
||||
style: context.dynamicTextStyle(
|
||||
fontSize: 12.f,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 12.h),
|
||||
|
||||
Text(
|
||||
"This monthly Health Summary Report reflects the health indicators and analysis results of the latest visits. Please note that this will be sent automatically from the system and it's not considered as an official report so no medical decisions should be taken based on it"
|
||||
.needTranslation,
|
||||
style: TextStyle(
|
||||
color: AppColors.textColor,
|
||||
fontSize: 10.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 12.h),
|
||||
|
||||
Image.asset('assets/images/jpg/report.jpg'),
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.successColor,
|
||||
foregroundColor: AppColors.whiteColor,
|
||||
elevation: 0,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
onPressed: _onSavePressed,
|
||||
child: Text(
|
||||
LocaleKeys.save.tr(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16.f,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
).paddingAll(16),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildToggle() {
|
||||
final value = isHealthSummaryEnabled;
|
||||
|
||||
return AbsorbPointer(
|
||||
absorbing: true,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
width: 50.h,
|
||||
height: 28.h,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: value
|
||||
? AppColors.lightGreenColor
|
||||
: AppColors.greyColor.withOpacity(0.3),
|
||||
),
|
||||
child: AnimatedAlign(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
alignment: value ? Alignment.centerRight : Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: Container(
|
||||
width: 22.h,
|
||||
height: 22.h,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: value
|
||||
? AppColors.textGreenColor
|
||||
: AppColors.greyTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/monthly_reports/terms_conditions_view_model.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
import '../../theme/colors.dart';
|
||||
import '../../widgets/appbar/app_bar_widget.dart';
|
||||
|
||||
class UserAgreementPage extends StatefulWidget {
|
||||
const UserAgreementPage({super.key});
|
||||
|
||||
@override
|
||||
State<UserAgreementPage> createState() => _UserAgreementPageState();
|
||||
}
|
||||
|
||||
class _UserAgreementPageState extends State<UserAgreementPage> {
|
||||
late final WebViewController _webViewController;
|
||||
bool _isLoading = true;
|
||||
String? _errorMessage;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_webViewController = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setBackgroundColor(const Color(0x00000000))
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onPageStarted: (_) {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
},
|
||||
onPageFinished: (_) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
},
|
||||
onWebResourceError: (error) {
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final vm =
|
||||
Provider.of<TermsConditionsViewModel>(context, listen: false);
|
||||
|
||||
vm.getTermsConditions(
|
||||
onSuccess: () {
|
||||
final htmlString = vm.termsConditionsHtml ?? '';
|
||||
|
||||
if (htmlString.isNotEmpty) {
|
||||
setState(() {
|
||||
_errorMessage = null;
|
||||
_isLoading = true;
|
||||
});
|
||||
_webViewController.loadHtmlString(htmlString);
|
||||
} else {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_errorMessage = 'لا توجد شروط متاحة حالياً'.needTranslation;
|
||||
});
|
||||
}
|
||||
},
|
||||
onError: (msg) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_errorMessage = msg;
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.scaffoldBgColor,
|
||||
appBar: CustomAppBar(
|
||||
onBackPressed: () => Navigator.of(context).pop(),
|
||||
onLanguageChanged: (_) {},
|
||||
hideLogoAndLang: true,
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
WebViewWidget(controller: _webViewController),
|
||||
|
||||
if (_errorMessage != null)
|
||||
Center(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
_errorMessage!,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: AppColors.primaryRedColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_isLoading)
|
||||
const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,239 @@
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.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/qr_parking/models/qr_parking_response_model.dart';
|
||||
|
||||
import '../../features/qr_parking/qr_parking_view_model.dart';
|
||||
import '../../theme/colors.dart';
|
||||
import '../../widgets/appbar/app_bar_widget.dart';
|
||||
import '../../widgets/chip/app_custom_chip_widget.dart';
|
||||
import 'package:maps_launcher/maps_launcher.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
||||
class ParkingSlot extends StatefulWidget {
|
||||
final QrParkingResponseModel model;
|
||||
|
||||
const ParkingSlot({
|
||||
super.key,
|
||||
required this.model,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ParkingSlot> createState() => _ParkingSlotState();
|
||||
}
|
||||
|
||||
class _ParkingSlotState extends State<ParkingSlot> {
|
||||
|
||||
void _openDirection() {
|
||||
final lat = widget.model.latitude;
|
||||
final lng = widget.model.longitude;
|
||||
|
||||
final valid = lat != null &&
|
||||
lng != null &&
|
||||
!(lat == 0.0 && lng == 0.0) &&
|
||||
lat >= -90 && lat <= 90 &&
|
||||
lng >= -180 && lng <= 180;
|
||||
|
||||
if (!valid) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Parking location not available")),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
MapsLauncher.launchCoordinates(lat, lng);
|
||||
}
|
||||
|
||||
Future<void> _resetDirection() async {
|
||||
final vm = context.read<QrParkingViewModel>();
|
||||
await vm.clearParking();
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
}
|
||||
|
||||
DateTime? _parseDotNetDate(String? value) {
|
||||
if (value == null || value.isEmpty) return null;
|
||||
|
||||
final regExp = RegExp(r'Date\((\d+)([+-]\d+)?\)');
|
||||
final match = regExp.firstMatch(value);
|
||||
if (match == null) return null;
|
||||
|
||||
final milliseconds = int.tryParse(match.group(1)!);
|
||||
if (milliseconds == null) return null;
|
||||
|
||||
return DateTime.fromMillisecondsSinceEpoch(milliseconds, isUtc: true)
|
||||
.toLocal();
|
||||
}
|
||||
|
||||
|
||||
String _formatPrettyDate(String? value) {
|
||||
final date = _parseDotNetDate(value);
|
||||
if (date == null) return '-';
|
||||
|
||||
const months = [
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
|
||||
];
|
||||
|
||||
final day = date.day;
|
||||
final month = months[date.month - 1];
|
||||
final year = date.year;
|
||||
|
||||
return "$day $month $year";
|
||||
}
|
||||
|
||||
|
||||
String _formatPrettyTime(String? value) {
|
||||
final date = _parseDotNetDate(value);
|
||||
if (date == null) return '-';
|
||||
|
||||
int hour = date.hour;
|
||||
final minute = date.minute.toString().padLeft(2, '0');
|
||||
|
||||
final isPM = hour >= 12;
|
||||
final period = isPM ? 'PM' : 'AM';
|
||||
|
||||
hour = hour % 12;
|
||||
if (hour == 0) hour = 12;
|
||||
|
||||
return "${hour.toString().padLeft(2, '0')}:$minute $period";
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.scaffoldBgColor,
|
||||
appBar: CustomAppBar(
|
||||
onBackPressed: () => Navigator.of(context).pop(),
|
||||
onLanguageChanged: (_) {},
|
||||
hideLogoAndLang: true,
|
||||
),
|
||||
body: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final maxW = constraints.maxWidth;
|
||||
final contentW = maxW > 600 ? 600.0 : maxW;
|
||||
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: SizedBox(
|
||||
width: contentW,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
|
||||
Container(
|
||||
width: double.infinity,
|
||||
decoration: RoundedRectangleBorder()
|
||||
.toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.r,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Parking Slot Details".needTranslation,
|
||||
style: TextStyle(
|
||||
fontSize: 16.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
Wrap(
|
||||
spacing: 4,
|
||||
runSpacing: 4,
|
||||
children: [
|
||||
AppCustomChipWidget(
|
||||
labelText:
|
||||
"Slot: ${widget.model.qRParkingCode ?? '-'}"
|
||||
.needTranslation,
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText:
|
||||
"Basement: ${widget.model.floorDescription ?? '-'}"
|
||||
.needTranslation,
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText:
|
||||
"Date: ${_formatPrettyDate(widget.model.createdOn)}"
|
||||
.needTranslation,
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText:
|
||||
"Parked Since: ${_formatPrettyTime(widget.model.createdOn)}"
|
||||
.needTranslation,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 24.h),
|
||||
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 48.h,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryRedColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
onPressed: _openDirection,
|
||||
child: Text(
|
||||
"Get Direction".needTranslation,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.whiteColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// const Spacer(),
|
||||
// SizedBox(
|
||||
// width: double.infinity,
|
||||
// height: 48.h,
|
||||
// child: OutlinedButton(
|
||||
// style: OutlinedButton.styleFrom(
|
||||
// side: BorderSide(color: AppColors.primaryRedColor),
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(10),
|
||||
// ),
|
||||
// ),
|
||||
// onPressed: _resetDirection,
|
||||
// child: Text(
|
||||
// "Reset Direction".needTranslation,
|
||||
// style: TextStyle(
|
||||
// fontSize: 16,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: AppColors.primaryRedColor,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,155 @@
|
||||
// import 'package:easy_localization/easy_localization.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
//
|
||||
// import '../../generated/locale_keys.g.dart';
|
||||
// import '../../theme/colors.dart';
|
||||
//
|
||||
// class ReminderTimerDialog extends StatefulWidget {
|
||||
// final Function()? onSetReminderPress;
|
||||
// final String message;
|
||||
//
|
||||
// const ReminderTimerDialog(this.onSetReminderPress, this.message, {super.key});
|
||||
//
|
||||
//
|
||||
// @override
|
||||
// State<ReminderTimerDialog> createState() => _ReminderTimerDialogState();
|
||||
// }
|
||||
//
|
||||
// class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
|
||||
// final List<String> options = ["Morning", "Afternoon", "Evening", "Midnight"];
|
||||
// final List<String> selectedTimes = ["Morning"]; // Default selection
|
||||
//
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return //
|
||||
// Column(
|
||||
// children: [
|
||||
// Container(
|
||||
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24,
|
||||
// hasShadow: true,),
|
||||
// child: Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// // Checkboxes list
|
||||
// children: options.map((time) => buildCircleCheckbox(time)).toList(),
|
||||
// ).paddingAll(16),
|
||||
// ),
|
||||
// const SizedBox(height: 25),
|
||||
// // Buttons Row
|
||||
// Row(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: ElevatedButton.icon(
|
||||
// onPressed: () => Navigator.pop(context),
|
||||
// icon: const Icon(Icons.close, color: AppColors.errorColor),
|
||||
// label: Text(
|
||||
// LocaleKeys.cancel.tr(),
|
||||
// style: TextStyle(
|
||||
// color: AppColors.errorColor,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// fontSize: 14
|
||||
// ),
|
||||
// ),
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// backgroundColor: AppColors.secondaryLightRedColor,
|
||||
// elevation: 0,
|
||||
// padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(12),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: 12),
|
||||
// Expanded(
|
||||
// child: ElevatedButton.icon(
|
||||
// onPressed: () {
|
||||
// Navigator.pop(context, selectedTimes);
|
||||
// },
|
||||
// icon: const Icon(Icons.notifications_rounded),
|
||||
// label: Text(
|
||||
// LocaleKeys.setReminder.tr(),
|
||||
// style: TextStyle(
|
||||
// fontWeight: FontWeight.w500,
|
||||
// fontSize: 14
|
||||
// ),
|
||||
// ),
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// backgroundColor: AppColors.successColor,
|
||||
// foregroundColor: AppColors.whiteColor,
|
||||
// elevation: 0,
|
||||
// padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(12),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// const SizedBox(height: 30),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// Widget buildCircleCheckbox(String label) {
|
||||
// final bool isSelected = selectedTimes.contains(label);
|
||||
// return InkWell(
|
||||
// onTap: () {
|
||||
// setState(() {
|
||||
// if (isSelected) {
|
||||
// selectedTimes.remove(label);
|
||||
// } else {
|
||||
// selectedTimes.add(label);
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// borderRadius: BorderRadius.circular(25),
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// // Custom circle checkbox
|
||||
// Container(
|
||||
// width: 15,
|
||||
// height: 15,
|
||||
// decoration: BoxDecoration(
|
||||
// shape: BoxShape.circle,
|
||||
// border: Border.all(
|
||||
// color: isSelected ? AppColors.spacerLineColor: AppColors.spacerLineColor,
|
||||
// width: 1,
|
||||
// ),
|
||||
// color: isSelected ? AppColors.errorColor: AppColors.transparent,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: 12),
|
||||
// // Label text
|
||||
// Text(
|
||||
// label,
|
||||
// style: const TextStyle(fontSize: 16, color: Colors.black87),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
//
|
||||
// void showCircleCheckboxDialog(BuildContext context) async {
|
||||
// final selected = await showDialog<List<String>>(
|
||||
// context: context,
|
||||
// builder: (context) => const ReminderTimerDialog(),
|
||||
// );
|
||||
//
|
||||
// if (selected != null && selected.isNotEmpty) {
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// SnackBar(content: Text('Reminders set for: ${selected.join(', ')}')),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
Loading…
Reference in New Issue