Fix: Prevent console message loop on maintenance updates

The console was generating excessive messages after schedule maintenance updates.
This commit is contained in:
Tola Leng
2025-05-26 16:42:05 +08:00
parent 23be1fca0f
commit e74bf35264
7 changed files with 175 additions and 94 deletions
@@ -58,7 +58,6 @@ export const useMaintenanceData = ({ refreshTrigger = 0 }: UseMaintenanceDataPro
// Prevent duplicate requests
if (currentRequestRef.current) {
console.log("Request already in progress, waiting...");
await currentRequestRef.current;
return;
}
@@ -72,14 +71,11 @@ export const useMaintenanceData = ({ refreshTrigger = 0 }: UseMaintenanceDataPro
const requestPromise = (async () => {
try {
console.log("Fetching maintenance data...", force ? "(forced)" : "");
const data = await maintenanceService.getMaintenanceRecords();
// Check if component is still mounted before updating state
if (!mountedRef.current) return;
console.log(`Fetched ${data.length} maintenance records`);
// Update state with fetched data
setAllMaintenanceData(data);
setInitialized(true);
@@ -119,7 +115,6 @@ export const useMaintenanceData = ({ refreshTrigger = 0 }: UseMaintenanceDataPro
// Initial fetch on mount - NO AUTOMATIC POLLING
useEffect(() => {
console.log("useMaintenanceData hook mounted, fetching initial data");
mountedRef.current = true;
// Only fetch initial data, no polling
@@ -134,7 +129,6 @@ export const useMaintenanceData = ({ refreshTrigger = 0 }: UseMaintenanceDataPro
// Handle refresh trigger changes - ONLY when explicitly triggered
useEffect(() => {
if (refreshTrigger > 0) {
console.log("Manual refresh triggered, forcing data fetch");
fetchMaintenanceData(true); // Force refresh to bypass cache
}
}, [refreshTrigger, fetchMaintenanceData]);
@@ -19,74 +19,157 @@ export const MaintenanceStatusChecker = ({
const { toast } = useToast();
const checkedItemsRef = useRef<Set<string>>(new Set());
const notificationSentRef = useRef<Set<string>>(new Set());
const intervalRef = useRef<number | null>(null);
useEffect(() => {
if (!maintenanceData || maintenanceData.length === 0) return;
// Check for maintenance items that need status update
const now = new Date();
const checkAndUpdateStatus = async () => {
const currentTime = new Date();
let hasUpdates = false;
console.log('MaintenanceStatusChecker: Checking status updates at', currentTime.toISOString());
console.log('MaintenanceStatusChecker: Checking', maintenanceData.length, 'maintenance items');
for (const item of maintenanceData) {
if (item.status.toLowerCase() !== 'scheduled') continue;
// Skip if we've already checked this item in this session
if (checkedItemsRef.current.has(item.id)) continue;
try {
const startTime = new Date(item.start_time);
const endTime = new Date(item.end_time);
const status = item.status.toLowerCase();
// If current time is past start time but before end time, update to in_progress
if (startTime <= now && now <= endTime) {
console.log(`Auto-updating maintenance ${item.title} to in_progress status`);
console.log(`MaintenanceStatusChecker: Item ${item.id} - Status: ${status}, Start: ${startTime.toISOString()}, End: ${endTime.toISOString()}, Current: ${currentTime.toISOString()}`);
// Check if scheduled maintenance should start (become in_progress)
if (status === 'scheduled' && currentTime >= startTime && currentTime <= endTime) {
const checkKey = `${item.id}-started`;
const notificationKey = `${item.id}-start-notification`;
// Update status to in_progress
await maintenanceService.updateMaintenanceStatus(item.id, 'in_progress');
// Only send notification if not sent before
if (!notificationSentRef.current.has(item.id)) {
console.log(`Sending start notification for maintenance ${item.title}`);
if (!checkedItemsRef.current.has(checkKey)) {
console.log(`MaintenanceStatusChecker: Starting maintenance ${item.id} at ${currentTime.toISOString()}`);
// Send notification for maintenance start
await maintenanceNotificationService.sendMaintenanceNotification({
maintenance: item,
notificationType: 'start'
// Update status to in_progress first
await maintenanceService.updateMaintenanceStatus(item.id, 'in_progress');
// Send start notification only once
if (!notificationSentRef.current.has(notificationKey)) {
try {
await maintenanceNotificationService.sendMaintenanceNotification({
maintenance: item,
notificationType: 'start'
});
notificationSentRef.current.add(notificationKey);
console.log(`MaintenanceStatusChecker: Start notification sent for ${item.id}`);
} catch (notificationError) {
console.log('MaintenanceStatusChecker: Start notification failed', notificationError);
}
}
toast({
title: t('maintenanceInProgress'),
description: `${item.title} ${t('isNowInProgress')}`,
});
// Mark as notified
notificationSentRef.current.add(item.id);
checkedItemsRef.current.add(checkKey);
hasUpdates = true;
}
toast({
title: t('maintenanceInProgress'),
description: `${item.title} ${t('isNowInProgress')}`,
});
onStatusUpdated();
}
// Mark as checked regardless of outcome
checkedItemsRef.current.add(item.id);
// Check if in_progress maintenance should be completed
if (status === 'in_progress' && currentTime >= endTime) {
const checkKey = `${item.id}-completed`;
const notificationKey = `${item.id}-end-notification`;
if (!checkedItemsRef.current.has(checkKey)) {
console.log(`MaintenanceStatusChecker: Completing maintenance ${item.id} at ${currentTime.toISOString()}`);
// Update status to completed first
await maintenanceService.updateMaintenanceStatus(item.id, 'completed');
// Send completion notification only once
if (!notificationSentRef.current.has(notificationKey)) {
try {
await maintenanceNotificationService.sendMaintenanceNotification({
maintenance: item,
notificationType: 'end'
});
notificationSentRef.current.add(notificationKey);
console.log(`MaintenanceStatusChecker: Completion notification sent for ${item.id}`);
} catch (notificationError) {
console.log('MaintenanceStatusChecker: Completion notification failed', notificationError);
}
}
toast({
title: t('maintenanceCompleted'),
description: `${item.title} ${t('hasBeenCompleted')}`,
});
checkedItemsRef.current.add(checkKey);
hasUpdates = true;
}
}
} catch (error) {
console.error(`Error auto-updating maintenance status for ${item.id}:`, error);
// Add a small delay before trying again to prevent rapid retry cycles
console.error('MaintenanceStatusChecker: Error updating status for item', item.id, error);
// Clear the check flags after 2 minutes to allow retry
setTimeout(() => {
checkedItemsRef.current.delete(item.id); // Allow retry on next check
}, 300000); // 5 minutes before retry
checkedItemsRef.current.delete(`${item.id}-started`);
checkedItemsRef.current.delete(`${item.id}-completed`);
notificationSentRef.current.delete(`${item.id}-start-notification`);
notificationSentRef.current.delete(`${item.id}-end-notification`);
}, 120000);
}
}
if (hasUpdates) {
console.log('MaintenanceStatusChecker: Status updates detected, triggering refresh');
// Force immediate refresh to update the UI
onStatusUpdated();
}
};
// Run the check immediately
// Clear the interval if it exists
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
// Initial check immediately
checkAndUpdateStatus();
// Set up interval to check every minute
const intervalId = setInterval(checkAndUpdateStatus, 60000);
// Check every 5 seconds for immediate status updates
intervalRef.current = window.setInterval(checkAndUpdateStatus, 5000);
return () => clearInterval(intervalId);
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
}, [maintenanceData, onStatusUpdated, t, toast]);
// This is a utility component with no UI render
// Clear check flags when maintenance data changes significantly
useEffect(() => {
const currentIds = new Set(maintenanceData.map(item => item.id));
// Clean up check flags for items that no longer exist
const keysToDelete = Array.from(checkedItemsRef.current).filter(key => {
const itemId = key.split('-')[0];
return !currentIds.has(itemId);
});
keysToDelete.forEach(key => {
checkedItemsRef.current.delete(key);
});
// Clean up notification flags for items that no longer exist
const notificationKeysToDelete = Array.from(notificationSentRef.current).filter(key => {
const itemId = key.split('-')[0];
return !currentIds.has(itemId);
});
notificationKeysToDelete.forEach(key => {
notificationSentRef.current.delete(key);
});
}, [maintenanceData]);
return null;
};
};
@@ -25,7 +25,6 @@ export const MaintenanceDetailContent = ({
queryKey: ['users'],
queryFn: async () => {
const usersList = await userService.getUsers();
console.log("Fetched users for maintenance detail:", usersList);
return usersList || [];
},
enabled: true
@@ -36,7 +35,6 @@ export const MaintenanceDetailContent = ({
queryKey: ['notificationChannels'],
queryFn: async () => {
const channels = await alertConfigService.getAlertConfigurations();
console.log("Fetched notification channels:", channels);
return channels || [];
},
enabled: true
@@ -45,9 +43,6 @@ export const MaintenanceDetailContent = ({
// Process user information when users data is available
useEffect(() => {
if (users.length > 0 && maintenance) {
console.log("Processing maintenance data with users:", maintenance);
console.log("Maintenance assigned_users:", maintenance.assigned_users);
// Create a copy of the maintenance object for modifications
const enhancedMaintenance = { ...maintenance };
@@ -58,7 +53,6 @@ export const MaintenanceDetailContent = ({
// Step 1: Extract user IDs from various formats
if (Array.isArray(maintenance.assigned_users)) {
userIds = maintenance.assigned_users;
console.log("Assigned users is an array:", userIds);
} else if (typeof maintenance.assigned_users === 'string') {
// Try parsing the string to extract user IDs
try {
@@ -67,34 +61,28 @@ export const MaintenanceDetailContent = ({
if (Array.isArray(parsedData)) {
// Direct array format
userIds = parsedData;
console.log("Parsed assigned_users from JSON array:", userIds);
} else if (typeof parsedData === 'string') {
// Nested JSON string
try {
const nestedData = JSON.parse(parsedData);
if (Array.isArray(nestedData)) {
userIds = nestedData;
console.log("Parsed assigned_users from nested JSON:", userIds);
}
} catch (e) {
// If nested parsing fails, treat as single ID
userIds = [parsedData];
console.log("Using parsed string as single ID:", userIds);
}
} else {
// Unknown format, use as is
userIds = [String(parsedData)];
console.log("Using parsed data as single ID:", userIds);
}
} catch (e) {
// If JSON parsing fails, try comma splitting
if (maintenance.assigned_users.includes(',')) {
userIds = maintenance.assigned_users.split(',').map(id => id.trim()).filter(Boolean);
console.log("Split assigned_users by comma:", userIds);
} else {
// Single ID
userIds = [maintenance.assigned_users];
console.log("Using string as single ID:", userIds);
}
}
}
@@ -107,20 +95,15 @@ export const MaintenanceDetailContent = ({
cleanId = cleanId.replace(/[\[\]"'\\]/g, '');
return cleanId;
}).filter(Boolean);
console.log("Cleaned user IDs:", userIds);
// Step 3: Find matching users from the users array
if (userIds.length > 0) {
const matchedUsers = users.filter(user => userIds.includes(user.id));
console.log("Matched assigned users:", matchedUsers);
setAssignedUsers(matchedUsers);
} else {
console.log("No user IDs found in assigned_users");
setAssignedUsers([]);
}
} else {
console.log("No assigned users found in maintenance data");
setAssignedUsers([]);
}
@@ -138,24 +121,15 @@ export const MaintenanceDetailContent = ({
const channelId = maintenance.notification_channel_id || maintenance.notification_id;
if (channelId && notificationChannels.length > 0) {
console.log("Looking for notification channel with ID:", channelId);
console.log("Available notification channels:", notificationChannels.map(c => ({ id: c.id, name: c.notify_name })));
const channel = notificationChannels.find(ch => ch.id === channelId);
if (channel) {
console.log("Found notification channel:", channel);
enhancedMaintenance.notification_channel_name = `${channel.notify_name} (${channel.notification_type})`;
} else {
console.log("No matching notification channel found for ID:", channelId);
enhancedMaintenance.notification_channel_name = `Channel ID: ${channelId}`;
}
} else {
console.log("No channel ID available or no notification channels loaded");
}
}
console.log("Enhanced maintenance with details:", enhancedMaintenance);
console.log("Assigned users for display:", assignedUsers);
setMaintenanceWithDetails(enhancedMaintenance);
}
}, [maintenance, users, notificationChannels]);
@@ -173,4 +147,4 @@ export const MaintenanceDetailContent = ({
/>
</>
);
};
};