Add alerts ssytems to the frontend
This commit is contained in:
172
frontend/src/hooks/useNotifications.ts
Normal file
172
frontend/src/hooks/useNotifications.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useSSE } from '../contexts/SSEContext';
|
||||
|
||||
export interface NotificationData {
|
||||
id: string;
|
||||
item_type: 'alert' | 'recommendation';
|
||||
severity: 'urgent' | 'high' | 'medium' | 'low';
|
||||
title: string;
|
||||
message: string;
|
||||
timestamp: string;
|
||||
read: boolean;
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'bakery-notifications';
|
||||
|
||||
const loadNotificationsFromStorage = (): NotificationData[] => {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored);
|
||||
return Array.isArray(parsed) ? parsed : [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to load notifications from localStorage:', error);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const saveNotificationsToStorage = (notifications: NotificationData[]) => {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(notifications));
|
||||
} catch (error) {
|
||||
console.warn('Failed to save notifications to localStorage:', error);
|
||||
}
|
||||
};
|
||||
|
||||
export const useNotifications = () => {
|
||||
const [notifications, setNotifications] = useState<NotificationData[]>(() => loadNotificationsFromStorage());
|
||||
const [unreadCount, setUnreadCount] = useState(() => {
|
||||
const stored = loadNotificationsFromStorage();
|
||||
return stored.filter(n => !n.read).length;
|
||||
});
|
||||
|
||||
const { addEventListener, isConnected } = useSSE();
|
||||
|
||||
// Save to localStorage whenever notifications change
|
||||
useEffect(() => {
|
||||
saveNotificationsToStorage(notifications);
|
||||
}, [notifications]);
|
||||
|
||||
useEffect(() => {
|
||||
// Listen for initial_items event (existing notifications)
|
||||
const removeInitialListener = addEventListener('initial_items', (data: any[]) => {
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
const initialNotifications: NotificationData[] = data.map(item => ({
|
||||
id: item.id,
|
||||
item_type: item.item_type,
|
||||
severity: item.severity,
|
||||
title: item.title,
|
||||
message: item.message,
|
||||
timestamp: item.timestamp || new Date().toISOString(),
|
||||
read: false, // Assume all initial items are unread
|
||||
}));
|
||||
|
||||
setNotifications(prev => {
|
||||
// Merge initial items with existing notifications, avoiding duplicates
|
||||
const existingIds = new Set(prev.map(n => n.id));
|
||||
const newNotifications = initialNotifications.filter(n => !existingIds.has(n.id));
|
||||
const combined = [...newNotifications, ...prev].slice(0, 50);
|
||||
return combined;
|
||||
});
|
||||
|
||||
setUnreadCount(prev => {
|
||||
const newUnreadCount = initialNotifications.filter(n => !n.read).length;
|
||||
return prev + newUnreadCount;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for alert events
|
||||
const removeAlertListener = addEventListener('alert', (data: any) => {
|
||||
const notification: NotificationData = {
|
||||
id: data.id,
|
||||
item_type: 'alert',
|
||||
severity: data.severity,
|
||||
title: data.title,
|
||||
message: data.message,
|
||||
timestamp: data.timestamp || new Date().toISOString(),
|
||||
read: false,
|
||||
};
|
||||
|
||||
setNotifications(prev => {
|
||||
// Check if notification already exists
|
||||
const exists = prev.some(n => n.id === notification.id);
|
||||
if (exists) return prev;
|
||||
|
||||
return [notification, ...prev].slice(0, 50); // Keep last 50 notifications
|
||||
});
|
||||
setUnreadCount(prev => prev + 1);
|
||||
});
|
||||
|
||||
// Listen for recommendation events
|
||||
const removeRecommendationListener = addEventListener('recommendation', (data: any) => {
|
||||
const notification: NotificationData = {
|
||||
id: data.id,
|
||||
item_type: 'recommendation',
|
||||
severity: data.severity,
|
||||
title: data.title,
|
||||
message: data.message,
|
||||
timestamp: data.timestamp || new Date().toISOString(),
|
||||
read: false,
|
||||
};
|
||||
|
||||
setNotifications(prev => {
|
||||
// Check if notification already exists
|
||||
const exists = prev.some(n => n.id === notification.id);
|
||||
if (exists) return prev;
|
||||
|
||||
return [notification, ...prev].slice(0, 50); // Keep last 50 notifications
|
||||
});
|
||||
setUnreadCount(prev => prev + 1);
|
||||
});
|
||||
|
||||
return () => {
|
||||
removeInitialListener();
|
||||
removeAlertListener();
|
||||
removeRecommendationListener();
|
||||
};
|
||||
}, [addEventListener]);
|
||||
|
||||
const markAsRead = (notificationId: string) => {
|
||||
setNotifications(prev =>
|
||||
prev.map(notification =>
|
||||
notification.id === notificationId
|
||||
? { ...notification, read: true }
|
||||
: notification
|
||||
)
|
||||
);
|
||||
setUnreadCount(prev => Math.max(0, prev - 1));
|
||||
};
|
||||
|
||||
const markAllAsRead = () => {
|
||||
setNotifications(prev =>
|
||||
prev.map(notification => ({ ...notification, read: true }))
|
||||
);
|
||||
setUnreadCount(0);
|
||||
};
|
||||
|
||||
const removeNotification = (notificationId: string) => {
|
||||
const notification = notifications.find(n => n.id === notificationId);
|
||||
setNotifications(prev => prev.filter(n => n.id !== notificationId));
|
||||
|
||||
if (notification && !notification.read) {
|
||||
setUnreadCount(prev => Math.max(0, prev - 1));
|
||||
}
|
||||
};
|
||||
|
||||
const clearAllNotifications = () => {
|
||||
setNotifications([]);
|
||||
setUnreadCount(0);
|
||||
};
|
||||
|
||||
return {
|
||||
notifications,
|
||||
unreadCount,
|
||||
isConnected,
|
||||
markAsRead,
|
||||
markAllAsRead,
|
||||
removeNotification,
|
||||
clearAll: clearAllNotifications,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user