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()); ever(projectSelectedRange, (_) => fetchProjectProgress());
} }
/// ========================= // =========================
/// Helper Methods // Helper Methods
/// ========================= // =========================
int _getDaysFromRange(String range) { int _getDaysFromRange(String range) {
switch (range) { switch (range) {
case '7D': case '7D':
@ -114,21 +114,28 @@ class DashboardController extends GetxController {
logSafe('Project chart view toggled to: $isChart', level: LogLevel.debug); logSafe('Project chart view toggled to: $isChart', level: LogLevel.debug);
} }
/// ========================= // =========================
/// Manual refresh // Manual Refresh Methods
/// ========================= // =========================
Future<void> refreshDashboard() async { Future<void> refreshDashboard() async {
logSafe('Manual dashboard refresh triggered.', level: LogLevel.debug); logSafe('Manual dashboard refresh triggered.', level: LogLevel.debug);
await fetchAllDashboardData(); await fetchAllDashboardData();
} }
/// ========================= Future<void> refreshAttendance() async => fetchRoleWiseAttendance();
/// Fetch all dashboard data 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 { Future<void> fetchAllDashboardData() async {
final String projectId = projectController.selectedProjectId.value; final String projectId = projectController.selectedProjectId.value;
// Skip fetching if no project is selected
if (projectId.isEmpty) { if (projectId.isEmpty) {
logSafe('No project selected. Skipping dashboard API calls.', logSafe('No project selected. Skipping dashboard API calls.',
level: LogLevel.warning); level: LogLevel.warning);
@ -143,17 +150,15 @@ class DashboardController extends GetxController {
]); ]);
} }
/// ========================= // =========================
/// API Calls // API Calls
/// ========================= // =========================
Future<void> fetchRoleWiseAttendance() async { Future<void> fetchRoleWiseAttendance() async {
final String projectId = projectController.selectedProjectId.value; final String projectId = projectController.selectedProjectId.value;
if (projectId.isEmpty) return; if (projectId.isEmpty) return;
try { try {
isAttendanceLoading.value = true; isAttendanceLoading.value = true;
final List<dynamic>? response = final List<dynamic>? response =
await ApiService.getDashboardAttendanceOverview( await ApiService.getDashboardAttendanceOverview(
projectId, getAttendanceDays()); projectId, getAttendanceDays());
@ -179,16 +184,12 @@ class DashboardController extends GetxController {
Future<void> fetchProjectProgress() async { Future<void> fetchProjectProgress() async {
final String projectId = projectController.selectedProjectId.value; final String projectId = projectController.selectedProjectId.value;
if (projectId.isEmpty) return; if (projectId.isEmpty) return;
try { try {
isProjectLoading.value = true; isProjectLoading.value = true;
final response = await ApiService.getProjectProgress( final response = await ApiService.getProjectProgress(
projectId: projectId, projectId: projectId, days: getProjectDays());
days: getProjectDays(),
);
if (response != null && response.success) { if (response != null && response.success) {
projectChartData.value = projectChartData.value =
@ -208,11 +209,10 @@ class DashboardController extends GetxController {
} }
Future<void> fetchDashboardTasks({required String projectId}) async { Future<void> fetchDashboardTasks({required String projectId}) async {
if (projectId.isEmpty) return; // Skip if empty if (projectId.isEmpty) return;
try { try {
isTasksLoading.value = true; isTasksLoading.value = true;
final response = await ApiService.getDashboardTasks(projectId: projectId); final response = await ApiService.getDashboardTasks(projectId: projectId);
if (response != null && response.success) { if (response != null && response.success) {
@ -235,11 +235,10 @@ class DashboardController extends GetxController {
} }
Future<void> fetchDashboardTeams({required String projectId}) async { Future<void> fetchDashboardTeams({required String projectId}) async {
if (projectId.isEmpty) return; // Skip if empty if (projectId.isEmpty) return;
try { try {
isTeamsLoading.value = true; isTeamsLoading.value = true;
final response = await ApiService.getDashboardTeams(projectId: projectId); final response = await ApiService.getDashboardTeams(projectId: projectId);
if (response != null && response.success) { 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/directory_controller.dart';
import 'package:marco/controller/directory/notes_controller.dart'; import 'package:marco/controller/directory/notes_controller.dart';
import 'package:marco/controller/document/user_document_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/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. /// Handles incoming FCM notification actions and updates UI/controllers.
class NotificationActionHandler { class NotificationActionHandler {
@ -46,6 +47,10 @@ class NotificationActionHandler {
break; break;
case 'attendance_updated': case 'attendance_updated':
_handleAttendanceUpdated(data); _handleAttendanceUpdated(data);
_handleDashboardUpdate(data); // refresh dashboard attendance
break;
case 'dashboard_update':
_handleDashboardUpdate(data); // full dashboard refresh
break; break;
default: default:
_logger.w('⚠️ Unknown notification type: $type'); _logger.w('⚠️ Unknown notification type: $type');
@ -60,16 +65,19 @@ class NotificationActionHandler {
case 'Attendance': case 'Attendance':
if (_isAttendanceAction(action)) { if (_isAttendanceAction(action)) {
_handleAttendanceUpdated(data); _handleAttendanceUpdated(data);
_handleDashboardUpdate(data);
} }
break; break;
/// 🔹 Tasks /// 🔹 Tasks
case 'Report_Task': case 'Report_Task':
_handleTaskUpdated(data, isComment: false); _handleTaskUpdated(data, isComment: false);
_handleDashboardUpdate(data);
break; break;
case 'Task_Comment': case 'Task_Comment':
_handleTaskUpdated(data, isComment: true); _handleTaskUpdated(data, isComment: true);
_handleDashboardUpdate(data);
break; break;
case 'Task_Modified': case 'Task_Modified':
@ -77,11 +85,13 @@ class NotificationActionHandler {
case 'Floor_Modified': case 'Floor_Modified':
case 'Building_Modified': case 'Building_Modified':
_handleTaskPlanningUpdated(data); _handleTaskPlanningUpdated(data);
_handleDashboardUpdate(data);
break; break;
/// 🔹 Expenses /// 🔹 Expenses
case 'Expenses_Modified': case 'Expenses_Modified':
_handleExpenseUpdated(data); _handleExpenseUpdated(data);
_handleDashboardUpdate(data);
break; break;
/// 🔹 Documents /// 🔹 Documents
@ -198,59 +208,55 @@ class NotificationActionHandler {
} }
/// ---------------------- DOCUMENT HANDLER ---------------------- /// ---------------------- DOCUMENT HANDLER ----------------------
/// ---------------------- DOCUMENT HANDLER ---------------------- static void _handleDocumentModified(Map<String, dynamic> data) {
static void _handleDocumentModified(Map<String, dynamic> data) { late String entityTypeId;
late String entityTypeId; late String entityId;
late String entityId; String? documentId = data['DocumentId'];
String? documentId = data['DocumentId'];
if (data['Keyword'] == 'Employee_Document_Modified') { if (data['Keyword'] == 'Employee_Document_Modified') {
entityTypeId = Permissions.employeeEntity; entityTypeId = Permissions.employeeEntity;
entityId = data['EmployeeId'] ?? ''; entityId = data['EmployeeId'] ?? '';
} else if (data['Keyword'] == 'Project_Document_Modified') { } else if (data['Keyword'] == 'Project_Document_Modified') {
entityTypeId = Permissions.projectEntity; entityTypeId = Permissions.projectEntity;
entityId = data['ProjectId'] ?? ''; entityId = data['ProjectId'] ?? '';
} else { } else {
_logger.w("⚠️ Document update received with unknown keyword: $data"); _logger.w("⚠️ Document update received with unknown keyword: $data");
return; return;
} }
if (entityId.isEmpty) { if (entityId.isEmpty) {
_logger.w("⚠️ Document update missing entityId: $data"); _logger.w("⚠️ Document update missing entityId: $data");
return; return;
} }
// 🔹 Refresh document list _safeControllerUpdate<DocumentController>(
_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>(
onFound: (controller) async { onFound: (controller) async {
if (controller.documentDetails.value?.data?.id == documentId) { await controller.fetchDocuments(
await controller.fetchDocumentDetails(documentId); entityTypeId: entityTypeId,
_logger.i("✅ DocumentDetailsController refreshed for Document $documentId"); entityId: entityId,
} reset: true,
);
}, },
notFoundMessage: ' DocumentDetailsController not active, skipping.', notFoundMessage: '⚠️ DocumentController not found, cannot refresh list.',
successMessage: '✅ DocumentDetailsController checked for refresh.', 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 ---------------------- /// ---------------------- DIRECTORY HANDLERS ----------------------
static void _handleContactModified(Map<String, dynamic> data) { static void _handleContactModified(Map<String, dynamic> data) {
_safeControllerUpdate<DirectoryController>( _safeControllerUpdate<DirectoryController>(
onFound: (controller) => controller.fetchContacts(), 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 ---------------------- /// ---------------------- UTILITY ----------------------
static void _safeControllerUpdate<T>({ static void _safeControllerUpdate<T>({