feat: Enhance dashboard refresh logic; add handling for various notification types and improve method organization

This commit is contained in:
Vaibhav Surve 2025-09-18 11:01:03 +05:30
parent 7d5d2b5bf4
commit 25b20fedda
2 changed files with 100 additions and 69 deletions

View File

@ -70,9 +70,9 @@ class DashboardController extends GetxController {
ever(projectSelectedRange, (_) => fetchProjectProgress());
}
/// =========================
/// Helper Methods
/// =========================
// =========================
// Helper Methods
// =========================
int _getDaysFromRange(String range) {
switch (range) {
case '7D':
@ -114,21 +114,28 @@ class DashboardController extends GetxController {
logSafe('Project chart view toggled to: $isChart', level: LogLevel.debug);
}
/// =========================
/// Manual refresh
/// =========================
// =========================
// Manual Refresh Methods
// =========================
Future<void> refreshDashboard() async {
logSafe('Manual dashboard refresh triggered.', level: LogLevel.debug);
await fetchAllDashboardData();
}
/// =========================
/// Fetch all dashboard data
/// =========================
Future<void> refreshAttendance() async => fetchRoleWiseAttendance();
Future<void> refreshTasks() async {
final projectId = projectController.selectedProjectId.value;
if (projectId.isNotEmpty) await fetchDashboardTasks(projectId: projectId);
}
Future<void> refreshProjects() async => fetchProjectProgress();
// =========================
// Fetch All Dashboard Data
// =========================
Future<void> fetchAllDashboardData() async {
final String projectId = projectController.selectedProjectId.value;
// Skip fetching if no project is selected
if (projectId.isEmpty) {
logSafe('No project selected. Skipping dashboard API calls.',
level: LogLevel.warning);
@ -143,17 +150,15 @@ class DashboardController extends GetxController {
]);
}
/// =========================
/// API Calls
/// =========================
// =========================
// API Calls
// =========================
Future<void> fetchRoleWiseAttendance() async {
final String projectId = projectController.selectedProjectId.value;
if (projectId.isEmpty) return;
try {
isAttendanceLoading.value = true;
final List<dynamic>? response =
await ApiService.getDashboardAttendanceOverview(
projectId, getAttendanceDays());
@ -179,16 +184,12 @@ class DashboardController extends GetxController {
Future<void> fetchProjectProgress() async {
final String projectId = projectController.selectedProjectId.value;
if (projectId.isEmpty) return;
try {
isProjectLoading.value = true;
final response = await ApiService.getProjectProgress(
projectId: projectId,
days: getProjectDays(),
);
projectId: projectId, days: getProjectDays());
if (response != null && response.success) {
projectChartData.value =
@ -208,11 +209,10 @@ class DashboardController extends GetxController {
}
Future<void> fetchDashboardTasks({required String projectId}) async {
if (projectId.isEmpty) return; // Skip if empty
if (projectId.isEmpty) return;
try {
isTasksLoading.value = true;
final response = await ApiService.getDashboardTasks(projectId: projectId);
if (response != null && response.success) {
@ -235,11 +235,10 @@ class DashboardController extends GetxController {
}
Future<void> fetchDashboardTeams({required String projectId}) async {
if (projectId.isEmpty) return; // Skip if empty
if (projectId.isEmpty) return;
try {
isTeamsLoading.value = true;
final response = await ApiService.getDashboardTeams(projectId: projectId);
if (response != null && response.success) {

View File

@ -9,8 +9,9 @@ import 'package:marco/controller/expense/expense_detail_controller.dart';
import 'package:marco/controller/directory/directory_controller.dart';
import 'package:marco/controller/directory/notes_controller.dart';
import 'package:marco/controller/document/user_document_controller.dart';
import 'package:marco/helpers/utils/permission_constants.dart';
import 'package:marco/controller/document/document_details_controller.dart';
import 'package:marco/controller/dashboard/dashboard_controller.dart';
import 'package:marco/helpers/utils/permission_constants.dart';
/// Handles incoming FCM notification actions and updates UI/controllers.
class NotificationActionHandler {
@ -46,6 +47,10 @@ class NotificationActionHandler {
break;
case 'attendance_updated':
_handleAttendanceUpdated(data);
_handleDashboardUpdate(data); // refresh dashboard attendance
break;
case 'dashboard_update':
_handleDashboardUpdate(data); // full dashboard refresh
break;
default:
_logger.w('⚠️ Unknown notification type: $type');
@ -60,16 +65,19 @@ class NotificationActionHandler {
case 'Attendance':
if (_isAttendanceAction(action)) {
_handleAttendanceUpdated(data);
_handleDashboardUpdate(data);
}
break;
/// 🔹 Tasks
case 'Report_Task':
_handleTaskUpdated(data, isComment: false);
_handleDashboardUpdate(data);
break;
case 'Task_Comment':
_handleTaskUpdated(data, isComment: true);
_handleDashboardUpdate(data);
break;
case 'Task_Modified':
@ -77,11 +85,13 @@ class NotificationActionHandler {
case 'Floor_Modified':
case 'Building_Modified':
_handleTaskPlanningUpdated(data);
_handleDashboardUpdate(data);
break;
/// 🔹 Expenses
case 'Expenses_Modified':
_handleExpenseUpdated(data);
_handleDashboardUpdate(data);
break;
/// 🔹 Documents
@ -198,59 +208,55 @@ class NotificationActionHandler {
}
/// ---------------------- DOCUMENT HANDLER ----------------------
/// ---------------------- DOCUMENT HANDLER ----------------------
static void _handleDocumentModified(Map<String, dynamic> data) {
late String entityTypeId;
late String entityId;
String? documentId = data['DocumentId'];
static void _handleDocumentModified(Map<String, dynamic> data) {
late String entityTypeId;
late String entityId;
String? documentId = data['DocumentId'];
if (data['Keyword'] == 'Employee_Document_Modified') {
entityTypeId = Permissions.employeeEntity;
entityId = data['EmployeeId'] ?? '';
} else if (data['Keyword'] == 'Project_Document_Modified') {
entityTypeId = Permissions.projectEntity;
entityId = data['ProjectId'] ?? '';
} else {
_logger.w("⚠️ Document update received with unknown keyword: $data");
return;
}
if (data['Keyword'] == 'Employee_Document_Modified') {
entityTypeId = Permissions.employeeEntity;
entityId = data['EmployeeId'] ?? '';
} else if (data['Keyword'] == 'Project_Document_Modified') {
entityTypeId = Permissions.projectEntity;
entityId = data['ProjectId'] ?? '';
} else {
_logger.w("⚠️ Document update received with unknown keyword: $data");
return;
}
if (entityId.isEmpty) {
_logger.w("⚠️ Document update missing entityId: $data");
return;
}
if (entityId.isEmpty) {
_logger.w("⚠️ Document update missing entityId: $data");
return;
}
// 🔹 Refresh document list
_safeControllerUpdate<DocumentController>(
onFound: (controller) async {
await controller.fetchDocuments(
entityTypeId: entityTypeId,
entityId: entityId,
reset: true,
);
},
notFoundMessage: '⚠️ DocumentController not found, cannot refresh list.',
successMessage: '✅ DocumentController refreshed from notification.',
);
// 🔹 Refresh document details (if opened)
if (documentId != null) {
_safeControllerUpdate<DocumentDetailsController>(
_safeControllerUpdate<DocumentController>(
onFound: (controller) async {
if (controller.documentDetails.value?.data?.id == documentId) {
await controller.fetchDocumentDetails(documentId);
_logger.i("✅ DocumentDetailsController refreshed for Document $documentId");
}
await controller.fetchDocuments(
entityTypeId: entityTypeId,
entityId: entityId,
reset: true,
);
},
notFoundMessage: ' DocumentDetailsController not active, skipping.',
successMessage: '✅ DocumentDetailsController checked for refresh.',
notFoundMessage: '⚠️ DocumentController not found, cannot refresh list.',
successMessage: '✅ DocumentController refreshed from notification.',
);
}
}
if (documentId != null) {
_safeControllerUpdate<DocumentDetailsController>(
onFound: (controller) async {
if (controller.documentDetails.value?.data?.id == documentId) {
await controller.fetchDocumentDetails(documentId);
_logger.i(
"✅ DocumentDetailsController refreshed for Document $documentId");
}
},
notFoundMessage: ' DocumentDetailsController not active, skipping.',
successMessage: '✅ DocumentDetailsController checked for refresh.',
);
}
}
/// ---------------------- DIRECTORY HANDLERS ----------------------
static void _handleContactModified(Map<String, dynamic> data) {
_safeControllerUpdate<DirectoryController>(
onFound: (controller) => controller.fetchContacts(),
@ -296,6 +302,32 @@ static void _handleDocumentModified(Map<String, dynamic> data) {
);
}
/// ---------------------- DASHBOARD HANDLER ----------------------
static void _handleDashboardUpdate(Map<String, dynamic> data) {
_safeControllerUpdate<DashboardController>(
onFound: (controller) async {
final type = data['type'] ?? '';
switch (type) {
case 'attendance_updated':
await controller.fetchRoleWiseAttendance();
break;
case 'task_updated':
await controller.fetchDashboardTasks(
projectId: controller.projectController.selectedProjectId.value);
break;
case 'project_progress_update':
await controller.fetchProjectProgress();
break;
case 'full_dashboard_refresh':
default:
await controller.refreshDashboard();
}
},
notFoundMessage: '⚠️ DashboardController not found, cannot refresh.',
successMessage: '✅ DashboardController refreshed from notification.',
);
}
/// ---------------------- UTILITY ----------------------
static void _safeControllerUpdate<T>({