You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cloudsolutions-atoms/lib/modules/cm_module/utilities/service_request_utils.dart

531 lines
19 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:device_calendar/device_calendar.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:google_api_availability/google_api_availability.dart';
import 'package:huawei_push/huawei_push.dart' as h_push;
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/notification/notification_manger.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
import '../service_request_detail_provider.dart';
class ServiceRequestUtils {
static double calculateAndAssignWorkingHours({
required DateTime? startTime,
required DateTime? endTime,
required TextEditingController workingHoursController,
required Function(double) updateModel, // A callback to update the model
}) {
if (startTime != null && endTime != null) {
Duration difference = endTime.difference(startTime);
double hours = difference.inMinutes / 60.0; // Calculate hours as a decimal
workingHoursController.text = hours.toStringAsFixed(1); // Format to 1 decimal places
updateModel(hours); // Call the function to update the model
return hours;
} else {
return -1; // Indicating invalid input
}
}
static bool isLocalUrl(String url) {
if (url.isEmpty != false) return false;
return url.startsWith("/") || url.startsWith("file://") || url.substring(1).startsWith(':\\');
}
static String calculateTimeDifference(DateTime startDate, DateTime endDate) {
try {
Duration diff = startDate.difference(endDate);
int days = diff.inDays;
int hours = diff.inHours.remainder(24);
int minutes = diff.inMinutes.remainder(60);
int seconds = diff.inSeconds.remainder(60);
List<String> parts = [];
if (days > 0) parts.add('$days ${days == 1 ? 'day' : 'days'}');
if (hours > 0) parts.add('$hours ${hours == 1 ? 'hour' : 'hours'}');
if (minutes > 0) parts.add('$minutes ${minutes == 1 ? 'minute' : 'minutes'}');
if (seconds > 0) parts.add('$seconds ${seconds == 1 ? 'second' : 'seconds'}');
String timeDifference = parts.isEmpty ? '' : parts.join(', ');
return 'Action duration: $timeDifference';
} catch (e) {
return '';
}
}
static String formatTimerDuration(int seconds) {
int hours = seconds ~/ 3600;
int minutes = (seconds % 3600) ~/ 60;
int remainingSeconds = seconds % 60;
String formattedDuration = '';
if (hours > 0) {
formattedDuration += '$hours hour${hours > 1 ? 's' : ''} ';
}
if (minutes > 0) {
formattedDuration += '$minutes minute${minutes > 1 ? 's' : ''} ';
}
if (remainingSeconds > 0) {
formattedDuration += '$remainingSeconds second${remainingSeconds > 1 ? 's' : ''} ';
}
if (formattedDuration.isEmpty) {
formattedDuration = 'Less than a second';
}
return formattedDuration.trim();
}
static String formatTimerDurationCM(int seconds) {
int hours = seconds ~/ 3600;
int minutes = (seconds % 3600) ~/ 60;
int remainingSeconds = seconds % 60;
String formattedDuration = '';
if (hours > 0) {
formattedDuration += '$hours hour${hours > 1 ? 's' : ''} ';
}
if (minutes > 0) {
formattedDuration += '$minutes minute${minutes > 1 ? 's' : ''} ';
}
if (remainingSeconds > 0) {
formattedDuration += '$remainingSeconds second${remainingSeconds > 1 ? 's' : ''} ';
}
if (formattedDuration.isEmpty) {
formattedDuration = 'Less than a minute';
}
return formattedDuration.trim();
}
static String formatTotalWorkingHours(double timeInHours) {
// Convert hours → seconds
int totalSeconds = (timeInHours * 3600).round();
int hours = totalSeconds ~/ 3600;
int minutes = (totalSeconds % 3600) ~/ 60;
int remainingSeconds = totalSeconds % 60;
String formattedDuration = '';
if (hours > 0) {
formattedDuration += '$hours hour${hours > 1 ? 's' : ''} ';
}
if (minutes > 0) {
formattedDuration += '$minutes minute${minutes > 1 ? 's' : ''} ';
}
if (remainingSeconds > 0) {
formattedDuration += '$remainingSeconds second${remainingSeconds > 1 ? 's' : ''} ';
}
if (formattedDuration.isEmpty) {
formattedDuration = 'Less than a minute';
}
return formattedDuration.trim();
}
static void getQrCode({required BuildContext context}) async {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
ServiceRequestDetailProvider requestDetailProvider = Provider.of<ServiceRequestDetailProvider>(context, listen: false);
String? base64String = await requestDetailProvider.getQrCode(workOrderId: requestDetailProvider.currentWorkOrder!.data!.requestId ?? 0);
Navigator.pop(context);
if (base64String != null) {
// You have the base64 string, now you can display it
Uint8List bytes = base64Decode(base64String);
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Colors.white,
title: context.translation.scanQr.heading6(context).center,
content: Image.memory(
bytes, // Displaying the QR code from base64
fit: BoxFit.contain, // Ensure the image fits well in the dialog
errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.error, color: Colors.red);
},
),
);
},
);
} else {
log('Failed to get the QR code');
}
}
static void scheduleOrUpdateNotification({required BuildContext context, required DateTime scheduleDateTime, required String workOrderId}) async {
DateTime scheduleTime = scheduleDateTime;
await NotificationManger.cancelNotificationById(workOrderId.hashCode);
NotificationManger.scheduleNotification(id: workOrderId.hashCode, title: 'Scheduled Notification', body: '$scheduleTime', scheduledNotificationDateTime: scheduleTime);
}
//steps:
//add permission for andriod..
//add string in info.plist for ios...
//tz.TZDateTime.from(movieScreening.dateTime, tz.local) some time you need to add this while making build..
//create event..
static void addOrUpdateEventToCalendar({
required BuildContext context,
required DateTime start,
required DateTime end,
required String title,
String? workOrderNumber,
}) async {
final deviceCalendarPlugin = DeviceCalendarPlugin();
final currentLocation = getLocation(await FlutterTimezone.getLocalTimezone());
TZDateTime startDate = TZDateTime.from(start, currentLocation);
TZDateTime endDate = TZDateTime.from(end, currentLocation);
setLocalLocation(currentLocation);
final calendarsResult = await deviceCalendarPlugin.retrieveCalendars();
if (calendarsResult.data?.isEmpty ?? true) {
'No calendars available on the device.'.showToast;
return;
}
// Select a write able calendar
final writableCalendar = calendarsResult.data?.firstWhere(
(calendar) => calendar.isReadOnly == false,
orElse: () => Calendar(id: '', name: 'Default', isReadOnly: true),
);
if (writableCalendar == null || writableCalendar.isReadOnly == true) {
log('No writable calendar found.');
'No writable calendar found.'.showToast;
return; // Handle the case where no writable calendar exists
}
final eventsResult = await deviceCalendarPlugin.retrieveEvents(
writableCalendar.id!,
RetrieveEventsParams(startDate: start.subtract(Duration(days: 7)), endDate: end.add(Duration(days: 30))),
);
if (!(eventsResult.isSuccess && eventsResult.data != null)) {
"Failed to retrieve reminders.".showToast;
return;
}
// Find an event with the matching workOrderNumber in the description
final matchingEvent = eventsResult.data!.firstWhere(
(event) => event.description?.contains('$workOrderNumber') ?? false,
orElse: () => Event('0', eventId: null),
);
if (matchingEvent.eventId != null) {
// Update the existing event
matchingEvent.title = title;
matchingEvent.start = startDate;
matchingEvent.end = endDate;
// Save the updated event
final updateResult = await deviceCalendarPlugin.createOrUpdateEvent(matchingEvent);
if (updateResult?.isSuccess == true) {
log('Reminder updated successfully: ${updateResult?.data}');
"Reminder updated successfully on device calendar".showToast;
} else {
log('Failed to update reminder: ${updateResult?.errors}');
"Failed to update reminder on device calendar".showToast;
}
} else {
// add event to calendar..
final eventToCreate = Event(
writableCalendar.id,
title: title,
description: 'You have a reminder for a visit for work order $workOrderNumber',
start: startDate,
end: endDate,
);
final createResult = await deviceCalendarPlugin.createOrUpdateEvent(eventToCreate);
if (createResult?.isSuccess == true) {
log('Reminder added successfully: ${createResult?.data}');
//TODO need to confirm to show the message to user...
'Reminder added to device calendar'.showToast;
} else {
log('Failed to add reminder: ${createResult?.errors}');
'Failed to add reminder to device calendar'.showToast;
}
}
}
// static void addEventToCalendar({
// required BuildContext context,
// required DateTime start,
// required DateTime end,
// required String title,
// String? workOrderNumber,
// }) async {
// final deviceCalendarPlugin = DeviceCalendarPlugin();
//
// // Retrieve current timezone
// final currentLocation = getLocation(await FlutterTimezone.getLocalTimezone());
// setLocalLocation(currentLocation);
//
// // Retrieve calendars
// final calendarsResult = await deviceCalendarPlugin.retrieveCalendars();
//
// if (calendarsResult.data?.isEmpty ?? true) {
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('No calendars available on the device.')),
// );
// return;
// }
//
// // Select a writable calendar
// final writableCalendar = calendarsResult.data?.firstWhere(
// (calendar) => calendar.isReadOnly == false,
// orElse: () => Calendar(id: '', name: 'Default', isReadOnly: true),
// );
//
// if (writableCalendar == null || writableCalendar.isReadOnly==true) {
// log('No writable calendar found.');
// return; // Handle the case where no writable calendar exists
// }
// // Convert start and end times
// TZDateTime startDate = TZDateTime.from(start, currentLocation);
// TZDateTime endDate = TZDateTime.from(end, currentLocation);
//
// // Create event
// final eventToCreate = Event(
// writableCalendar.id,
// title: title,
// description: 'You have a reminder for a visit for work order $workOrderNumber ',
// start: startDate,
// end: endDate,
// );
//
// // Create or update the event
// final response = await deviceCalendarPlugin.createOrUpdateEvent(eventToCreate);
//
// if (response?.isSuccess == true) {
// log('Event added successfully: ${response?.data}');
// } else {
// log('Failed to add event: ${response?.errors}');
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('Failed to add event to calendar.')),
// );
// }
// }
// static Future<void> updateEventOnCalendar({
// required BuildContext context,
// required String calendarId,
// required int requestId,
// required String updatedTitle,
// required DateTime updatedStart,
// required DateTime updatedEnd,
// }) async {
// final deviceCalendarPlugin = DeviceCalendarPlugin();
//
// // Retrieve all events in the calendar
// final eventsResult = await deviceCalendarPlugin.retrieveEvents(
// calendarId,
// RetrieveEventsParams(),
// );
//
// if (!(eventsResult.isSuccess && eventsResult.data != null)) {
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('Failed to retrieve events.')),
// );
// return;
// }
//
// // Find the event with the matching requestId
// final matchingEvent = eventsResult.data!.firstWhere(
// (event) => event.eventId == requestId.toString(),
// orElse: () => null,
// );
//
// if (matchingEvent == null) {
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('Event not found for requestId: $requestId')),
// );
// return;
// }
//
// // Update the event's details
// matchingEvent.title = updatedTitle;
// matchingEvent.start = updatedStart;
// matchingEvent.end = updatedEnd;
//
// // Save the updated event
// final updateResult = await deviceCalendarPlugin.createOrUpdateEvent(matchingEvent);
//
// if (updateResult?.isSuccess == true) {
// log('Event updated successfully: ${updateResult?.data}');
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('Event updated successfully.')),
// );
// } else {
// log('Failed to update event: ${updateResult?.errors}');
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('Failed to update event.')),
// );
// }
// }
// static void addEventToCalendar({required BuildContext context, required DateTime start, required DateTime end,required String title,required int ? requestId}) async {
//
//
// var currentLocation = getLocation(await FlutterTimezone.getLocalTimezone());
// final calendarsResult = await DeviceCalendarPlugin().retrieveCalendars();
//
// if (calendarsResult.data?.isNotEmpty ?? false) {
// setLocalLocation(currentLocation);
// await Future.delayed(const Duration(seconds: 2));
// TZDateTime startDate = TZDateTime.from(start, currentLocation);
// TZDateTime endDate = TZDateTime.from(end, currentLocation);
// final calendarId = calendarsResult.data!.first.id; // Use the first calendar
//
// var eventToCreate = Event(
// calendarId,
// eventId: requestId.toString(),
// title: title,
// description: "You have a reminder for a visit",
// start: startDate,
// end: endDate,
// );
// final response = await DeviceCalendarPlugin().createOrUpdateEvent(eventToCreate);
// log("Response of adding calendar is ${response?.data}");
// } else {
// // "No calendars available on the device.".showToast();
//
// }
// // setLocalLocation(currentLocation);
// // await Future.delayed(const Duration(seconds: 2));
// // TZDateTime startDate = TZDateTime.from(start, currentLocation);
// // TZDateTime endDate = TZDateTime.from(end, currentLocation);
// // var eventToCreate = Event(
// // '0',
// // // eventId:requestId.toString(),
// // title: title,
// // description: "You have reminder for Visit",
// // start: startDate,
// // end: endDate,
// // );
// // log('event to add i got is ${eventToCreate.toJson()}');
// // final response = await DeviceCalendarPlugin().createOrUpdateEvent(eventToCreate);
// // log("response of adding calander is ${response?.data}");
// //
// }
// Map<String, dynamic> messageData = message.data;
// if (messageData["notificationType"] != null && messageData["accept"] != null) {
// if (messageData["notificationType"] == "arrivalConfirmation") {
// if ((messageData["accept"].toString()) == "False") {
// if(messageData["accept"])
// Navigator.pop(context, true);
// } else {
// Navigator.pop(context, false);
// }
// }
// }
static Future<bool?> listenForApproval() async {
log('listen for approval called..');
bool isVerified = false;
Completer<bool> completer = Completer<bool>();
if (Platform.isAndroid && !(await isGoogleServicesAvailable())) {
// If not using Firebase (i.e., if Google Services is unavailable)
h_push.Push.onMessageReceivedStream.listen((h_push.RemoteMessage remoteMessage) {
ConfirmArrivalNotificationModel notificationModel = ConfirmArrivalNotificationModel.fromJson(remoteMessage.toMap());
if (notificationModel.requestId != null && notificationModel.accept != null) {
if (notificationModel.accept == 'True') {
isVerified = true;
completer.complete(isVerified);
} else if (notificationModel.accept == 'False') {
isVerified = false;
completer.complete(isVerified);
}
}
}, onError: (Object error) {
log("onMessageReceivedStream:${error.toString()}");
completer.complete(false); // Handle any error
});
} else {
// Using Firebase
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
log('message data received: ${message.data}');
ConfirmArrivalNotificationModel notificationModel = ConfirmArrivalNotificationModel.fromJson(message.data);
if (notificationModel.requestId != null && notificationModel.accept != null) {
if (notificationModel.accept == 'True') {
isVerified = true;
if (!completer.isCompleted) {
completer.complete(isVerified);
}
} else if (notificationModel.accept == 'False') {
isVerified = false;
if (!completer.isCompleted) {
completer.complete(isVerified);
}
}
}
});
}
// Wait for the future to complete with the notification response
return completer.future;
}
static Future<bool> isGoogleServicesAvailable() async {
GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability();
String status = availability.toString().split('.').last;
if (status == "success") {
return true;
}
return false;
}
}
class ConfirmArrivalNotificationModel {
final String? transactionType;
final String? requestId;
final String? notificationType;
final String? userId;
final String? accept;
ConfirmArrivalNotificationModel({
this.transactionType,
this.requestId,
this.notificationType,
this.userId,
this.accept,
});
factory ConfirmArrivalNotificationModel.fromJson(Map<String, dynamic> json) {
return ConfirmArrivalNotificationModel(
transactionType: json['transactionType'],
requestId: json['requestId'],
notificationType: json['notificationType'],
userId: json['userId'],
accept: json['accept'],
);
}
// Convert an instance to a Map (e.g., for serialization)
Map<String, dynamic> toJson() {
return {
'transactionType': transactionType,
'requestId': requestId,
'notificationType': notificationType,
'userId': userId,
'accept': accept,
};
}
}