refactor: Clean up attendance controller initialization and enhance project change handling

This commit is contained in:
Vaibhav Surve 2025-11-03 16:43:51 +05:30
parent ebc8996f71
commit c78c14e409
4 changed files with 63 additions and 25 deletions

View File

@ -51,11 +51,6 @@ class AttendanceController extends GetxController {
super.onInit(); super.onInit();
_initializeDefaults(); _initializeDefaults();
// 🔹 Fetch organizations for the selected project
final projectId = Get.find<ProjectController>().selectedProject?.id;
if (projectId != null) {
fetchOrganizations(projectId);
}
} }
void _initializeDefaults() { void _initializeDefaults() {

View File

@ -1,6 +1,6 @@
class ApiEndpoints { class ApiEndpoints {
static const String baseUrl = "https://stageapi.marcoaiot.com/api"; // static const String baseUrl = "https://stageapi.marcoaiot.com/api";
// static const String baseUrl = "https://api.marcoaiot.com/api"; static const String baseUrl = "https://api.marcoaiot.com/api";
// static const String baseUrl = "https://devapi.marcoaiot.com/api"; // static const String baseUrl = "https://devapi.marcoaiot.com/api";
// Dashboard Module API Endpoints // Dashboard Module API Endpoints

View File

@ -69,7 +69,6 @@ class NotificationActionHandler {
} }
break; break;
case 'Team_Modified': case 'Team_Modified':
// Call method to handle team modifications and dashboard update
_handleDashboardUpdate(data); _handleDashboardUpdate(data);
break; break;
@ -129,6 +128,11 @@ class NotificationActionHandler {
/// ---------------------- HANDLERS ---------------------- /// ---------------------- HANDLERS ----------------------
static void _handleTaskPlanningUpdated(Map<String, dynamic> data) { static void _handleTaskPlanningUpdated(Map<String, dynamic> data) {
if (!_isCurrentProject(data)) {
_logger.i(" Ignored task planning update from another project.");
return;
}
final projectId = data['ProjectId']; final projectId = data['ProjectId'];
if (projectId == null) { if (projectId == null) {
_logger.w("⚠️ TaskPlanning update received without ProjectId: $data"); _logger.w("⚠️ TaskPlanning update received without ProjectId: $data");
@ -159,13 +163,17 @@ class NotificationActionHandler {
} }
static void _handleExpenseUpdated(Map<String, dynamic> data) { static void _handleExpenseUpdated(Map<String, dynamic> data) {
if (!_isCurrentProject(data)) {
_logger.i(" Ignored expense update from another project.");
return;
}
final expenseId = data['ExpenseId']; final expenseId = data['ExpenseId'];
if (expenseId == null) { if (expenseId == null) {
_logger.w("⚠️ Expense update received without ExpenseId: $data"); _logger.w("⚠️ Expense update received without ExpenseId: $data");
return; return;
} }
// Update Expense List
_safeControllerUpdate<ExpenseController>( _safeControllerUpdate<ExpenseController>(
onFound: (controller) async { onFound: (controller) async {
await controller.fetchExpenses(); await controller.fetchExpenses();
@ -175,7 +183,6 @@ class NotificationActionHandler {
'✅ ExpenseController refreshed from expense notification.', '✅ ExpenseController refreshed from expense notification.',
); );
// Update Expense Detail (if open and matches this expenseId)
_safeControllerUpdate<ExpenseDetailController>( _safeControllerUpdate<ExpenseDetailController>(
onFound: (controller) async { onFound: (controller) async {
if (controller.expense.value?.id == expenseId) { if (controller.expense.value?.id == expenseId) {
@ -190,6 +197,11 @@ class NotificationActionHandler {
} }
static void _handleAttendanceUpdated(Map<String, dynamic> data) { static void _handleAttendanceUpdated(Map<String, dynamic> data) {
if (!_isCurrentProject(data)) {
_logger.i(" Ignored attendance update from another project.");
return;
}
_safeControllerUpdate<AttendanceController>( _safeControllerUpdate<AttendanceController>(
onFound: (controller) => controller.refreshDataFromNotification( onFound: (controller) => controller.refreshDataFromNotification(
projectId: data['ProjectId'], projectId: data['ProjectId'],
@ -201,6 +213,11 @@ class NotificationActionHandler {
static void _handleTaskUpdated(Map<String, dynamic> data, static void _handleTaskUpdated(Map<String, dynamic> data,
{required bool isComment}) { {required bool isComment}) {
if (!_isCurrentProject(data)) {
_logger.i(" Ignored task update from another project.");
return;
}
_safeControllerUpdate<DailyTaskController>( _safeControllerUpdate<DailyTaskController>(
onFound: (controller) => controller.refreshTasksFromNotification( onFound: (controller) => controller.refreshTasksFromNotification(
projectId: data['ProjectId'], projectId: data['ProjectId'],
@ -213,11 +230,15 @@ class NotificationActionHandler {
/// ---------------------- DOCUMENT HANDLER ---------------------- /// ---------------------- DOCUMENT HANDLER ----------------------
static void _handleDocumentModified(Map<String, dynamic> data) { static void _handleDocumentModified(Map<String, dynamic> data) {
if (!_isCurrentProject(data)) {
_logger.i(" Ignored document update from another project.");
return;
}
String entityTypeId; String entityTypeId;
String entityId; String entityId;
String? documentId = data['DocumentId']; String? documentId = data['DocumentId'];
// Determine entity type and ID
if (data['Keyword'] == 'Employee_Document_Modified') { if (data['Keyword'] == 'Employee_Document_Modified') {
entityTypeId = Permissions.employeeEntity; entityTypeId = Permissions.employeeEntity;
entityId = data['EmployeeId'] ?? ''; entityId = data['EmployeeId'] ?? '';
@ -237,7 +258,6 @@ class NotificationActionHandler {
_logger.i( _logger.i(
"🔔 Document notification received: keyword=${data['Keyword']}, entityTypeId=$entityTypeId, entityId=$entityId, documentId=$documentId"); "🔔 Document notification received: keyword=${data['Keyword']}, entityTypeId=$entityTypeId, entityId=$entityId, documentId=$documentId");
// Refresh Document List
if (Get.isRegistered<DocumentController>()) { if (Get.isRegistered<DocumentController>()) {
_safeControllerUpdate<DocumentController>( _safeControllerUpdate<DocumentController>(
onFound: (controller) async { onFound: (controller) async {
@ -255,11 +275,9 @@ class NotificationActionHandler {
_logger.w('⚠️ DocumentController not registered, skipping list refresh.'); _logger.w('⚠️ DocumentController not registered, skipping list refresh.');
} }
// Refresh Document Details (if open)
if (documentId != null && Get.isRegistered<DocumentDetailsController>()) { if (documentId != null && Get.isRegistered<DocumentDetailsController>()) {
_safeControllerUpdate<DocumentDetailsController>( _safeControllerUpdate<DocumentDetailsController>(
onFound: (controller) async { onFound: (controller) async {
// Refresh details regardless of current document
await controller.fetchDocumentDetails(documentId); await controller.fetchDocumentDetails(documentId);
_logger.i( _logger.i(
"✅ DocumentDetailsController refreshed for Document $documentId"); "✅ DocumentDetailsController refreshed for Document $documentId");
@ -276,13 +294,10 @@ class NotificationActionHandler {
/// ---------------------- DIRECTORY HANDLERS ---------------------- /// ---------------------- DIRECTORY HANDLERS ----------------------
static void _handleContactModified(Map<String, dynamic> data) { static void _handleContactModified(Map<String, dynamic> data) {
final contactId = data['ContactId'];
// Always refresh the contact list
_safeControllerUpdate<DirectoryController>( _safeControllerUpdate<DirectoryController>(
onFound: (controller) { onFound: (controller) {
controller.fetchContacts(); controller.fetchContacts();
// If a specific contact is provided, refresh its notes as well final contactId = data['ContactId'];
if (contactId != null) { if (contactId != null) {
controller.fetchCommentsForContact(contactId); controller.fetchCommentsForContact(contactId);
} }
@ -293,7 +308,6 @@ class NotificationActionHandler {
'✅ Directory contacts (and notes if applicable) refreshed from notification.', '✅ Directory contacts (and notes if applicable) refreshed from notification.',
); );
// Refresh notes globally as well
_safeControllerUpdate<NotesController>( _safeControllerUpdate<NotesController>(
onFound: (controller) => controller.fetchNotes(), onFound: (controller) => controller.fetchNotes(),
notFoundMessage: '⚠️ NotesController not found, cannot refresh notes.', notFoundMessage: '⚠️ NotesController not found, cannot refresh notes.',
@ -302,7 +316,6 @@ class NotificationActionHandler {
} }
static void _handleContactNoteModified(Map<String, dynamic> data) { static void _handleContactNoteModified(Map<String, dynamic> data) {
// Refresh both contacts and notes when a note is modified
_handleContactModified(data); _handleContactModified(data);
} }
@ -324,6 +337,11 @@ class NotificationActionHandler {
/// ---------------------- DASHBOARD HANDLER ---------------------- /// ---------------------- DASHBOARD HANDLER ----------------------
static void _handleDashboardUpdate(Map<String, dynamic> data) { static void _handleDashboardUpdate(Map<String, dynamic> data) {
if (!_isCurrentProject(data)) {
_logger.i(" Ignored dashboard update from another project.");
return;
}
_safeControllerUpdate<DashboardController>( _safeControllerUpdate<DashboardController>(
onFound: (controller) async { onFound: (controller) async {
final type = data['type'] ?? ''; final type = data['type'] ?? '';
@ -347,11 +365,9 @@ class NotificationActionHandler {
controller.projectController.selectedProjectId.value; controller.projectController.selectedProjectId.value;
final projectIdsString = data['ProjectIds'] ?? ''; final projectIdsString = data['ProjectIds'] ?? '';
// Convert comma-separated string to List<String>
final notificationProjectIds = final notificationProjectIds =
projectIdsString.split(',').map((e) => e.trim()).toList(); projectIdsString.split(',').map((e) => e.trim()).toList();
// Refresh only if current project ID is in the list
if (notificationProjectIds.contains(currentProjectId)) { if (notificationProjectIds.contains(currentProjectId)) {
await controller.fetchDashboardTeams(projectId: currentProjectId); await controller.fetchDashboardTeams(projectId: currentProjectId);
} }
@ -375,6 +391,24 @@ class NotificationActionHandler {
/// ---------------------- UTILITY ---------------------- /// ---------------------- UTILITY ----------------------
static bool _isCurrentProject(Map<String, dynamic> data) {
try {
final dashboard = Get.find<DashboardController>();
final currentProjectId =
dashboard.projectController.selectedProjectId.value;
final notificationProjectId = data['ProjectId']?.toString();
if (notificationProjectId == null || notificationProjectId.isEmpty) {
return true; // No project info allow global refresh
}
return notificationProjectId == currentProjectId;
} catch (e) {
_logger.w("⚠️ Could not verify project context: $e");
return true;
}
}
static void _safeControllerUpdate<T>({ static void _safeControllerUpdate<T>({
required void Function(T controller) onFound, required void Function(T controller) onFound,
required String notFoundMessage, required String notFoundMessage,

View File

@ -34,12 +34,12 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
// Listen for future project selection changes // 🔁 Listen for project changes
ever<String>(projectController.selectedProjectId, (projectId) async { ever<String>(projectController.selectedProjectId, (projectId) async {
if (projectId.isNotEmpty) await _loadData(projectId); if (projectId.isNotEmpty) await _loadData(projectId);
}); });
// Load initial data // 🚀 Load initial data only once the screen is shown
final projectId = projectController.selectedProjectId.value; final projectId = projectController.selectedProjectId.value;
if (projectId.isNotEmpty) _loadData(projectId); if (projectId.isNotEmpty) _loadData(projectId);
}); });
@ -402,4 +402,13 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
), ),
); );
} }
@override
void dispose() {
// 🧹 Clean up the controller when user leaves this screen
if (Get.isRegistered<AttendanceController>()) {
Get.delete<AttendanceController>();
}
super.dispose();
}
} }