- Replaced the custom delete confirmation dialog with a reusable ConfirmDialog widget for better code organization and reusability. - Improved the add expense bottom sheet by implementing form validation using a GlobalKey and TextFormField. - Enhanced user experience by adding validation for required fields and specific formats (e.g., GST, transaction ID). - Updated the expense list to reflect changes in the confirmation dialog and improved the handling of attachments. - Cleaned up code by removing unnecessary comments and ensuring consistent formatting.
184 lines
5.8 KiB
Dart
184 lines
5.8 KiB
Dart
import 'package:get/get.dart';
|
||
import 'package:logger/logger.dart';
|
||
|
||
import 'package:marco/controller/dashboard/attendance_screen_controller.dart';
|
||
import 'package:marco/controller/dashboard/daily_task_controller.dart';
|
||
import 'package:marco/controller/task_planing/daily_task_planing_controller.dart';
|
||
import 'package:marco/controller/expense/expense_screen_controller.dart';
|
||
import 'package:marco/controller/expense/expense_detail_controller.dart';
|
||
|
||
/// Handles incoming FCM notification actions and updates UI/controllers.
|
||
class NotificationActionHandler {
|
||
static final Logger _logger = Logger();
|
||
|
||
/// Main entry point — call this for any notification `data` map.
|
||
static void handle(Map<String, dynamic> data) {
|
||
_logger.i('📲 Handling notification action: $data');
|
||
|
||
if (data.isEmpty) {
|
||
_logger.w('⚠️ Empty notification data received.');
|
||
return;
|
||
}
|
||
|
||
final type = data['type'];
|
||
final action = data['Action'];
|
||
final keyword = data['Keyword'];
|
||
|
||
if (type != null) {
|
||
_handleByType(type, data);
|
||
} else if (keyword != null) {
|
||
_handleByKeyword(keyword, action, data);
|
||
} else {
|
||
_logger.w('⚠️ Unhandled notification: $data');
|
||
}
|
||
}
|
||
|
||
/// Handle notification if identified by `type`
|
||
static void _handleByType(String type, Map<String, dynamic> data) {
|
||
switch (type) {
|
||
case 'expense_updated':
|
||
// No specific handler yet
|
||
break;
|
||
case 'attendance_updated':
|
||
_handleAttendanceUpdated(data);
|
||
break;
|
||
default:
|
||
_logger.w('⚠️ Unknown notification type: $type');
|
||
}
|
||
}
|
||
|
||
/// Handle notification if identified by `keyword`
|
||
static void _handleByKeyword(
|
||
String keyword, String? action, Map<String, dynamic> data) {
|
||
switch (keyword) {
|
||
case 'Attendance':
|
||
if (_isAttendanceAction(action)) {
|
||
_handleAttendanceUpdated(data);
|
||
}
|
||
break;
|
||
|
||
case 'Report_Task':
|
||
_handleTaskUpdated(data, isComment: false);
|
||
break;
|
||
|
||
case 'Task_Comment':
|
||
_handleTaskUpdated(data, isComment: true);
|
||
break;
|
||
case 'Expenses_Modified':
|
||
_handleExpenseUpdated(data);
|
||
break;
|
||
|
||
// ✅ New cases
|
||
case 'Task_Modified':
|
||
case 'WorkArea_Modified':
|
||
case 'Floor_Modified':
|
||
case 'Building_Modified':
|
||
_handleTaskPlanningUpdated(data);
|
||
break;
|
||
|
||
default:
|
||
_logger.w('⚠️ Unhandled notification keyword: $keyword');
|
||
}
|
||
}
|
||
|
||
static void _handleTaskPlanningUpdated(Map<String, dynamic> data) {
|
||
final projectId = data['ProjectId'];
|
||
if (projectId == null) {
|
||
_logger.w("⚠️ TaskPlanning update received without ProjectId: $data");
|
||
return;
|
||
}
|
||
|
||
_safeControllerUpdate<DailyTaskPlaningController>(
|
||
onFound: (controller) {
|
||
controller.fetchTaskData(projectId);
|
||
},
|
||
notFoundMessage:
|
||
'⚠️ DailyTaskPlaningController not found, cannot refresh.',
|
||
successMessage:
|
||
'✅ DailyTaskPlaningController refreshed from notification.',
|
||
);
|
||
}
|
||
|
||
/// Validates the set of allowed Attendance actions
|
||
static bool _isAttendanceAction(String? action) {
|
||
const validActions = {
|
||
'CHECK_IN',
|
||
'CHECK_OUT',
|
||
'REQUEST_REGULARIZE',
|
||
'REQUEST_DELETE',
|
||
'REGULARIZE',
|
||
'REGULARIZE_REJECT'
|
||
};
|
||
return validActions.contains(action);
|
||
}
|
||
|
||
static void _handleExpenseUpdated(Map<String, dynamic> data) {
|
||
final expenseId = data['ExpenseId'];
|
||
if (expenseId == null) {
|
||
_logger.w("⚠️ Expense update received without ExpenseId: $data");
|
||
return;
|
||
}
|
||
|
||
// Update Expense List
|
||
_safeControllerUpdate<ExpenseController>(
|
||
onFound: (controller) async {
|
||
await controller.fetchExpenses();
|
||
},
|
||
notFoundMessage: '⚠️ ExpenseController not found, cannot refresh list.',
|
||
successMessage:
|
||
'✅ ExpenseController refreshed from expense notification.',
|
||
);
|
||
|
||
// Update Expense Detail (if open and matches this expenseId)
|
||
_safeControllerUpdate<ExpenseDetailController>(
|
||
onFound: (controller) async {
|
||
// only refresh if the open screen is for this expense
|
||
if (controller.expense.value?.id == expenseId) {
|
||
await controller.fetchExpenseDetails();
|
||
_logger
|
||
.i("✅ ExpenseDetailController refreshed for Expense $expenseId");
|
||
}
|
||
},
|
||
notFoundMessage: 'ℹ️ ExpenseDetailController not active, skipping.',
|
||
successMessage: '✅ ExpenseDetailController checked for refresh.',
|
||
);
|
||
}
|
||
|
||
static void _handleAttendanceUpdated(Map<String, dynamic> data) {
|
||
_safeControllerUpdate<AttendanceController>(
|
||
onFound: (controller) => controller.refreshDataFromNotification(
|
||
projectId: data['ProjectId'],
|
||
),
|
||
notFoundMessage: '⚠️ AttendanceController not found, cannot update.',
|
||
successMessage: '✅ AttendanceController refreshed from notification.',
|
||
);
|
||
}
|
||
|
||
static void _handleTaskUpdated(Map<String, dynamic> data,
|
||
{required bool isComment}) {
|
||
_safeControllerUpdate<DailyTaskController>(
|
||
onFound: (controller) => controller.refreshTasksFromNotification(
|
||
projectId: data['ProjectId'],
|
||
taskAllocationId: data['TaskAllocationId'],
|
||
),
|
||
notFoundMessage: '⚠️ DailyTaskController not found, cannot update.',
|
||
successMessage: '✅ DailyTaskController refreshed from notification.',
|
||
);
|
||
}
|
||
|
||
/// Generic reusable method for safe GetX controller access + log handling
|
||
static void _safeControllerUpdate<T>({
|
||
required void Function(T controller) onFound,
|
||
required String notFoundMessage,
|
||
required String successMessage,
|
||
}) {
|
||
try {
|
||
final controller = Get.find<T>();
|
||
onFound(controller);
|
||
_logger.i(successMessage);
|
||
} catch (e) {
|
||
_logger.w(notFoundMessage);
|
||
}
|
||
}
|
||
}
|