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();
_initializeDefaults();
// 🔹 Fetch organizations for the selected project
final projectId = Get.find<ProjectController>().selectedProject?.id;
if (projectId != null) {
fetchOrganizations(projectId);
}
}
void _initializeDefaults() {

View File

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

View File

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

View File

@ -34,12 +34,12 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
// Listen for future project selection changes
// 🔁 Listen for project changes
ever<String>(projectController.selectedProjectId, (projectId) async {
if (projectId.isNotEmpty) await _loadData(projectId);
});
// Load initial data
// 🚀 Load initial data only once the screen is shown
final projectId = projectController.selectedProjectId.value;
if (projectId.isNotEmpty) _loadData(projectId);
});
@ -47,7 +47,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
Future<void> _loadData(String projectId) async {
try {
attendanceController.selectedTab = 'todaysAttendance';
attendanceController.selectedTab = 'todaysAttendance';
await attendanceController.loadAttendanceData(projectId);
attendanceController.update(['attendance_dashboard_controller']);
} catch (e) {
@ -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();
}
}