// frontend/src/components/alerts/AlertDashboard.tsx /** * Main dashboard component for alerts and recommendations * Provides filtering, bulk actions, and real-time updates */ import React, { useState, useEffect, useMemo } from 'react'; import { AlertItem, ItemFilters, ItemType, ItemSeverity, ItemStatus } from '../../types/alerts'; import { useAlertStream } from '../../hooks/useAlertStream'; import { AlertCard } from './AlertCard'; import { AlertFilters } from './AlertFilters'; import { AlertStats } from './AlertStats'; import { ConnectionStatus } from './ConnectionStatus'; import { useTenantId } from '../../hooks/useTenantId'; interface AlertDashboardProps { className?: string; maxItems?: number; autoRequestNotifications?: boolean; } export const AlertDashboard: React.FC = ({ className = '', maxItems = 50, autoRequestNotifications = true }) => { const tenantId = useTenantId(); const { items, connectionState, urgentCount, highCount, recCount, acknowledgeItem, resolveItem, notificationPermission, requestNotificationPermission } = useAlertStream({ tenantId }); const [filters, setFilters] = useState({ item_type: 'all', severity: 'all', status: 'all', service: 'all', search: '' }); const [selectedItems, setSelectedItems] = useState([]); const [bulkActionsOpen, setBulkActionsOpen] = useState(false); const [viewMode, setViewMode] = useState<'list' | 'compact'>('list'); // Request notification permission on mount if needed useEffect(() => { if (autoRequestNotifications && notificationPermission === 'default') { // Delay request to avoid immediate popup const timer = setTimeout(() => { requestNotificationPermission(); }, 2000); return () => clearTimeout(timer); } }, [autoRequestNotifications, notificationPermission, requestNotificationPermission]); // Filter items based on current filters const filteredItems = useMemo(() => { let filtered = items; // Filter by type if (filters.item_type !== 'all') { filtered = filtered.filter(item => item.item_type === filters.item_type); } // Filter by severity if (filters.severity !== 'all') { filtered = filtered.filter(item => item.severity === filters.severity); } // Filter by status if (filters.status !== 'all') { filtered = filtered.filter(item => item.status === filters.status); } // Filter by service if (filters.service !== 'all') { filtered = filtered.filter(item => item.service === filters.service); } // Filter by search text if (filters.search.trim()) { const searchLower = filters.search.toLowerCase(); filtered = filtered.filter(item => item.title.toLowerCase().includes(searchLower) || item.message.toLowerCase().includes(searchLower) || item.type.toLowerCase().includes(searchLower) ); } return filtered.slice(0, maxItems); }, [items, filters, maxItems]); // Get unique services for filter dropdown const availableServices = useMemo(() => { const services = [...new Set(items.map(item => item.service))].sort(); return services; }, [items]); // Handle bulk actions const handleBulkAcknowledge = async () => { await Promise.all(selectedItems.map(id => acknowledgeItem(id))); setSelectedItems([]); setBulkActionsOpen(false); }; const handleBulkResolve = async () => { await Promise.all(selectedItems.map(id => resolveItem(id))); setSelectedItems([]); setBulkActionsOpen(false); }; const handleSelectAll = () => { const selectableItems = filteredItems .filter(item => item.status === 'active') .map(item => item.id); setSelectedItems(selectableItems); }; const handleClearSelection = () => { setSelectedItems([]); setBulkActionsOpen(false); }; const toggleItemSelection = (itemId: string) => { setSelectedItems(prev => prev.includes(itemId) ? prev.filter(id => id !== itemId) : [...prev, itemId] ); }; const activeItems = filteredItems.filter(item => item.status === 'active'); const hasSelection = selectedItems.length > 0; return (
{/* Header */}

Sistema de Alertas y Recomendaciones

Monitoreo en tiempo real de operaciones de panadería

{/* Connection Status */}
{/* Stats */} {/* Notification Permission Banner */} {notificationPermission === 'denied' && (

Notificaciones bloqueadas

Las notificaciones del navegador están deshabilitadas. No recibirás alertas urgentes en tiempo real.

)} {/* Filters and View Controls */}
{/* View Mode Toggle */}
{/* Bulk Actions */} {activeItems.length > 0 && (
)}
{/* Bulk Actions Panel */} {bulkActionsOpen && activeItems.length > 0 && (
{selectedItems.length} elementos seleccionados
{hasSelection && (
)}
)}
{/* Items List */}
{filteredItems.length === 0 ? (
{items.length === 0 ? (

Sistema operativo

No hay alertas activas en este momento. Todas las operaciones funcionan correctamente.

) : (

No se encontraron elementos

Intenta ajustar los filtros para ver más elementos.

)}
) : (
{filteredItems.map((item) => (
{/* Selection Checkbox */} {bulkActionsOpen && item.status === 'active' && (
toggleItemSelection(item.id)} className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
)}
))}
)}
); };