'use client' import { useState, useMemo } from 'react' import { RefreshCw, Grid, List, Filter } from 'lucide-react' import KPICards from '@/components/dashboard/KPICards' import DeviceGrid from '@/components/dashboard/DeviceGrid' import AlertsFeed from '@/components/dashboard/AlertsFeed' import { useSelectedClient } from '@/components/providers/SelectedClientProvider' import { cn } from '@/lib/utils' import { trpc } from '@/lib/trpc-client' type DeviceForGrid = { id: string nombre: string tipo: string estado: string ip?: string | null sistemaOperativo?: string | null lastSeen?: Date | null cpuUsage?: number | null ramUsage?: number | null cliente?: { nombre: string } } type DashboardAlert = { id: string severidad: 'INFO' | 'WARNING' | 'CRITICAL' estado: 'ACTIVA' | 'RECONOCIDA' | 'RESUELTA' titulo: string mensaje: string createdAt: Date dispositivo: { nombre: string } cliente: { nombre: string } } // Mock data - en produccion vendria de la API const mockStats = { totalDispositivos: 127, dispositivosOnline: 98, dispositivosOffline: 24, dispositivosAlerta: 5, alertasActivas: 8, alertasCriticas: 2, sesionesActivas: 3, } const mockDevices = [ { id: '1', nombre: 'SRV-PRINCIPAL', tipo: 'SERVIDOR', estado: 'ONLINE', ip: '192.168.1.10', sistemaOperativo: 'Windows Server 2022', lastSeen: new Date(), cpuUsage: 45, ramUsage: 72, }, { id: '2', nombre: 'PC-ADMIN-01', tipo: 'PC', estado: 'ONLINE', ip: '192.168.1.101', sistemaOperativo: 'Windows 11 Pro', lastSeen: new Date(), cpuUsage: 23, ramUsage: 56, }, { id: '3', nombre: 'LAPTOP-VENTAS', tipo: 'LAPTOP', estado: 'ALERTA', ip: '192.168.1.105', sistemaOperativo: 'Windows 11 Pro', lastSeen: new Date(Date.now() - 1000 * 60 * 5), cpuUsage: 95, ramUsage: 88, }, { id: '4', nombre: 'ROUTER-PRINCIPAL', tipo: 'ROUTER', estado: 'ONLINE', ip: '192.168.1.1', sistemaOperativo: 'RouterOS 7.12', lastSeen: new Date(), cpuUsage: null, ramUsage: null, }, { id: '5', nombre: 'SW-CORE-01', tipo: 'SWITCH', estado: 'ONLINE', ip: '192.168.1.2', sistemaOperativo: 'Cisco IOS', lastSeen: new Date(), cpuUsage: null, ramUsage: null, }, { id: '6', nombre: 'CELULAR-GERENTE', tipo: 'CELULAR', estado: 'ONLINE', ip: null, sistemaOperativo: 'Android 14', lastSeen: new Date(), cpuUsage: null, ramUsage: null, }, { id: '7', nombre: 'SRV-BACKUP', tipo: 'SERVIDOR', estado: 'OFFLINE', ip: '192.168.1.11', sistemaOperativo: 'Ubuntu 22.04', lastSeen: new Date(Date.now() - 1000 * 60 * 60 * 2), cpuUsage: null, ramUsage: null, }, { id: '8', nombre: 'AP-OFICINA-01', tipo: 'AP', estado: 'ONLINE', ip: '192.168.1.50', sistemaOperativo: 'UniFi AP', lastSeen: new Date(), cpuUsage: null, ramUsage: null, }, ] const mockAlerts = [ { id: '1', severidad: 'CRITICAL' as const, estado: 'ACTIVA' as const, titulo: 'Servidor de backup offline', mensaje: 'El servidor SRV-BACKUP no responde desde hace 2 horas', createdAt: new Date(Date.now() - 1000 * 60 * 120), dispositivo: { nombre: 'SRV-BACKUP' }, cliente: { nombre: 'Cliente A' }, }, { id: '2', severidad: 'WARNING' as const, estado: 'ACTIVA' as const, titulo: 'CPU alta', mensaje: 'Uso de CPU al 95% en LAPTOP-VENTAS', createdAt: new Date(Date.now() - 1000 * 60 * 15), dispositivo: { nombre: 'LAPTOP-VENTAS' }, cliente: { nombre: 'Cliente A' }, }, { id: '3', severidad: 'INFO' as const, estado: 'RECONOCIDA' as const, titulo: 'Actualizacion disponible', mensaje: 'Windows Update pendiente en PC-ADMIN-01', createdAt: new Date(Date.now() - 1000 * 60 * 60), dispositivo: { nombre: 'PC-ADMIN-01' }, cliente: { nombre: 'Cliente A' }, }, ] const DEVICES_LIMIT = 12 export default function DashboardPage() { const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid') const utils = trpc.useUtils() const { selectedClientId } = useSelectedClient() const clienteId = selectedClientId ?? undefined const statsQuery = trpc.clientes.dashboardStats.useQuery( { clienteId }, { refetchOnWindowFocus: false } ) const stats = statsQuery.data ?? mockStats const alertsQuery = trpc.alertas.list.useQuery( { page: 1, limit: 25, clienteId }, { refetchOnWindowFocus: false } ) const alerts: DashboardAlert[] = useMemo(() => { const list = alertsQuery.data?.alertas ?? [] return list.map((a) => ({ id: a.id, severidad: a.severidad, estado: a.estado, titulo: a.titulo, mensaje: a.mensaje, createdAt: a.createdAt instanceof Date ? a.createdAt : new Date(a.createdAt), dispositivo: a.dispositivo ? { nombre: a.dispositivo.nombre } : { nombre: '—' }, cliente: { nombre: a.cliente.nombre }, })) }, [alertsQuery.data]) const acknowledgeMutation = trpc.alertas.reconocer.useMutation({ onSuccess: () => { utils.alertas.list.invalidate() utils.clientes.dashboardStats.invalidate() }, }) const resolveMutation = trpc.alertas.resolver.useMutation({ onSuccess: () => { utils.alertas.list.invalidate() utils.clientes.dashboardStats.invalidate() }, }) const equiposQuery = trpc.equipos.list.useQuery( { page: 1, limit: DEVICES_LIMIT, clienteId }, { refetchOnWindowFocus: false } ) const redQuery = trpc.red.list.useQuery( { page: 1, limit: DEVICES_LIMIT, clienteId }, { refetchOnWindowFocus: false } ) const celularesQuery = trpc.celulares.list.useQuery( { page: 1, limit: DEVICES_LIMIT, clienteId }, { refetchOnWindowFocus: false } ) const devices: DeviceForGrid[] = useMemo(() => { const eq = equiposQuery.data?.dispositivos ?? [] const rd = redQuery.data?.dispositivos ?? [] const cel = celularesQuery.data?.dispositivos ?? [] const all = [...eq, ...rd, ...cel] return all.map((d) => ({ id: d.id, nombre: d.nombre, tipo: d.tipo, estado: d.estado, ip: d.ip ?? null, sistemaOperativo: d.sistemaOperativo ?? null, lastSeen: d.lastSeen ?? null, cpuUsage: d.cpuUsage ?? null, ramUsage: d.ramUsage ?? null, cliente: d.cliente ? { nombre: d.cliente.nombre } : undefined, })) }, [equiposQuery.data, redQuery.data, celularesQuery.data]) const devicesLoading = equiposQuery.isLoading || redQuery.isLoading || celularesQuery.isLoading const isRefreshing = statsQuery.isFetching || alertsQuery.isFetching || equiposQuery.isFetching || redQuery.isFetching || celularesQuery.isFetching const handleRefresh = async () => { await Promise.all([ statsQuery.refetch(), alertsQuery.refetch(), equiposQuery.refetch(), redQuery.refetch(), celularesQuery.refetch(), ]) } const handleDeviceAction = (deviceId: string, action: string) => { console.log(`Action ${action} on device ${deviceId}`) // TODO: Implementar acciones } const handleAcknowledgeAlert = (alertId: string) => { acknowledgeMutation.mutate({ id: alertId }) } const handleResolveAlert = (alertId: string) => { resolveMutation.mutate({ id: alertId }) } return (
{/* Header */}

Dashboard

Vision general del sistema

{/* KPI Cards */} {/* Main content */}
{/* Devices */}

Dispositivos

{devicesLoading ? (
Cargando dispositivos...
) : devices.length === 0 ? (
No hay dispositivos. Agregue clientes y sincronice con MeshCentral, LibreNMS o Headwind.
) : ( )}
{/* Alerts */}
{alertsQuery.isLoading ? (
Cargando alertas...
) : ( )}
) }