Files
MSP-CAS/src/app/(dashboard)/rendimiento/page.tsx

222 lines
6.6 KiB
TypeScript

'use client'
import { useState, useMemo, useEffect, useCallback } from 'react'
import { useSelectedClient } from '@/components/providers/SelectedClientProvider'
import { trpc } from '@/lib/trpc-client'
import MetricCard from '@/components/performance/MetricCard'
import ProcessTable from '@/components/performance/ProcessTable'
import type { ProcessItem } from '@/components/performance/ProcessRow'
const CHART_POINTS = 20
const POLL_INTERVAL_MS = 2000
function randomIn(min: number, max: number) {
return Math.round(min + Math.random() * (max - min))
}
function generateMockProcesses(): ProcessItem[] {
const names = [
'chrome.exe',
'Code.exe',
'node.exe',
'System',
'svchost.exe',
'explorer.exe',
'MsMpEng.exe',
'SearchHost.exe',
'RuntimeBroker.exe',
'dllhost.exe',
]
return names.slice(0, 10).map((name, i) => ({
id: `p-${i}`,
name,
pid: 1000 + i * 100 + randomIn(0, 99),
cpu: randomIn(0, 45),
memory: `${randomIn(50, 800)} MB`,
state: i % 3 === 0 ? 'En ejecución' : 'Activo',
}))
}
export default function RendimientoPage() {
const { selectedClientId } = useSelectedClient()
const clienteId = selectedClientId ?? undefined
const listQuery = trpc.equipos.list.useQuery(
{ clienteId, limit: 100 },
{ refetchOnWindowFocus: false }
)
const devices = useMemo(
() => (listQuery.data?.dispositivos ?? []).map((d) => ({ id: d.id, nombre: d.nombre })),
[listQuery.data]
)
const [selectedDeviceId, setSelectedDeviceId] = useState<string>('')
const [metrics, setMetrics] = useState({
cpu: 0,
memory: 0,
disk: 0,
network: 0,
})
const [chartHistory, setChartHistory] = useState<{
cpu: number[]
memory: number[]
disk: number[]
network: number[]
}>({
cpu: [],
memory: [],
disk: [],
network: [],
})
const [processes, setProcesses] = useState<ProcessItem[]>([])
const [loading, setLoading] = useState(false)
const hasDevice = !!selectedDeviceId
const tick = useCallback(() => {
const cpu = randomIn(15, 85)
const memory = randomIn(40, 90)
const disk = randomIn(25, 65)
const network = randomIn(5, 120)
setMetrics({ cpu, memory, disk, network })
setChartHistory((prev) => ({
cpu: [...prev.cpu, cpu].slice(-CHART_POINTS),
memory: [...prev.memory, memory].slice(-CHART_POINTS),
disk: [...prev.disk, disk].slice(-CHART_POINTS),
network: [...prev.network, network].slice(-CHART_POINTS),
}))
setProcesses(generateMockProcesses())
}, [])
useEffect(() => {
if (!hasDevice) {
setMetrics({ cpu: 0, memory: 0, disk: 0, network: 0 })
setChartHistory({ cpu: [], memory: [], disk: [], network: [] })
setProcesses([])
return
}
setLoading(true)
tick()
const t = setTimeout(() => setLoading(false), 400)
const interval = setInterval(tick, POLL_INTERVAL_MS)
return () => {
clearTimeout(t)
clearInterval(interval)
}
}, [hasDevice, tick])
const cpuFooter = hasDevice
? [
{ label: 'Procesos:', value: '142' },
{ label: 'Hilos:', value: '2,840' },
{ label: 'Velocidad:', value: '2.90 GHz' },
]
: [
{ label: 'Procesos:', value: '—' },
{ label: 'Hilos:', value: '—' },
{ label: 'Velocidad:', value: '—' },
]
const ramFooter = hasDevice
? [
{ label: 'En uso:', value: `${Math.round((metrics.memory / 100) * 16)} GB` },
{ label: 'Disponible:', value: `${Math.round(((100 - metrics.memory) / 100) * 16)} GB` },
{ label: 'Total:', value: '16 GB' },
]
: [
{ label: 'En uso:', value: '—' },
{ label: 'Disponible:', value: '—' },
{ label: 'Total:', value: '—' },
]
const diskFooter = hasDevice
? [
{ label: 'Lectura:', value: '12.5 MB/s' },
{ label: 'Escritura:', value: '3.2 MB/s' },
{ label: 'Activo:', value: 'Sí' },
]
: [
{ label: 'Lectura:', value: '—' },
{ label: 'Escritura:', value: '—' },
{ label: 'Activo:', value: '—' },
]
const networkFooter = hasDevice
? [
{ label: 'Enviado:', value: '1.2 GB' },
{ label: 'Recibido:', value: '4.8 GB' },
{ label: 'Adaptador:', value: 'Ethernet' },
]
: [
{ label: 'Enviado:', value: '—' },
{ label: 'Recibido:', value: '—' },
{ label: 'Adaptador:', value: '—' },
]
return (
<div className="space-y-6">
<header className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div>
<h1 className="text-2xl font-bold text-white sm:text-3xl">
Rendimiento en Tiempo Real
</h1>
<p className="mt-1 text-gray-400">
Monitorea recursos del sistema
</p>
</div>
<div className="shrink-0">
<select
value={selectedDeviceId}
onChange={(e) => setSelectedDeviceId(e.target.value)}
className="w-64 rounded-lg border border-white/10 bg-dark-300 px-4 py-2.5 text-sm text-gray-200 transition-colors hover:border-white/20 focus:border-cyan-500/50 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 disabled:opacity-50 disabled:cursor-not-allowed"
>
<option value="">-- Seleccionar dispositivo --</option>
{devices.map((d) => (
<option key={d.id} value={d.id}>
{d.nombre}
</option>
))}
</select>
</div>
</header>
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<MetricCard
title="CPU"
value={hasDevice ? `${metrics.cpu}` : '—'}
valueSuffix="%"
footerStats={cpuFooter}
chartData={chartHistory.cpu}
highUsage={metrics.cpu > 80}
/>
<MetricCard
title="Memoria RAM"
value={hasDevice ? `${metrics.memory}` : '—'}
valueSuffix="%"
footerStats={ramFooter}
chartData={chartHistory.memory}
highUsage={metrics.memory > 80}
/>
<MetricCard
title="Disco"
value={hasDevice ? `${metrics.disk}` : '—'}
valueSuffix="%"
footerStats={diskFooter}
chartData={chartHistory.disk}
highUsage={metrics.disk > 80}
/>
<MetricCard
title="Red"
value={hasDevice ? `${metrics.network}` : '—'}
valueSuffix="Mbps"
footerStats={networkFooter}
chartData={chartHistory.network}
/>
</div>
<ProcessTable processes={processes} noDevice={!hasDevice} />
</div>
)
}