import React, { useState, useEffect, useRef, useCallback } from 'react'; import axios from 'axios'; import { formatLocalTime } from './utils/timeUtils'; import './CommandCenterPages.css'; function Notifications() { const [notifications, setNotifications] = useState([]); const [unreadCount, setUnreadCount] = useState(0); const [filter, setFilter] = useState('all'); const [loading, setLoading] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [total, setTotal] = useState(0); const [pendingClear, setPendingClear] = useState(false); const itemsPerPage = 50; const wsRef = useRef(null); const wsReconnectTimer = useRef(null); const wsReconnectDelay = useRef(1000); const loadNotifications = useCallback(async () => { setLoading(true); try { const params = { limit: itemsPerPage, offset: (currentPage - 1) * itemsPerPage }; if (filter === 'unread') params.read = 'unread'; if (filter === 'read') params.read = 'read'; const response = await axios.get('/api/notifications', { params }); setNotifications(response.data.notifications); setUnreadCount(response.data.unreadCount); setTotal(response.data.total); } catch (error) { // Notifications load error handled } finally { setLoading(false); } }, [filter, currentPage]); const connectWebSocket = useCallback(() => { try { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const ws = new WebSocket(`${protocol}//${window.location.host}`); ws.onmessage = (event) => { try { const message = JSON.parse(event.data); if (message.type === 'notification') { loadNotifications(); } } catch (_) {} }; ws.onclose = () => { wsRef.current = null; wsReconnectTimer.current = setTimeout(() => { wsReconnectDelay.current = Math.min(wsReconnectDelay.current * 2, 30000); connectWebSocket(); }, wsReconnectDelay.current); }; ws.onopen = () => { wsReconnectDelay.current = 1000; }; ws.onerror = () => {}; wsRef.current = ws; } catch (_) {} }, [loadNotifications]); useEffect(() => { loadNotifications(); const interval = setInterval(loadNotifications, 10000); connectWebSocket(); return () => { clearInterval(interval); clearTimeout(wsReconnectTimer.current); if (wsRef.current) wsRef.current.close(); }; }, [filter, currentPage, loadNotifications, connectWebSocket]); const handleMarkAsRead = async (notificationId) => { try { await axios.put(`/api/notifications/${notificationId}/read`); loadNotifications(); } catch (error) { console.error('Failed to mark notification as read:', error); } }; const handleDelete = async (notificationId) => { try { await axios.delete(`/api/notifications/${notificationId}`); loadNotifications(); } catch (error) { console.error('Failed to delete notification:', error); } }; const handleClearAll = async () => { try { await axios.post('/api/notifications/clear', { type: filter }); setCurrentPage(1); setPendingClear(false); loadNotifications(); } catch (error) { setPendingClear(false); } }; const totalPages = Math.ceil(total / itemsPerPage); return (

Notifications

{notifications.length > 0 && ( pendingClear ? (
) : ( ) )}
{loading ? (

Loading...

) : notifications.length === 0 ? (

No {filter === 'unread' ? 'unread ' : filter === 'read' ? 'read ' : ''}notifications

) : ( <>
{notifications.map((notification) => (

{notification.title}

{notification.message}

{notification.resource_name && ( {notification.resource_name} )} {formatLocalTime(notification.created_at)}
{!notification.read && ( )}
))}
{/* Pagination */} {totalPages > 1 && (
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => ( ))}
)} )}
); } export default Notifications;