import { useQuery } from '@tanstack/react-query'
import { Activity, AlertTriangle, Bell, Cpu, Database, ShieldAlert } from 'lucide-react'
import {
PieChart,
Pie,
Cell,
Tooltip as ChartTooltip,
BarChart,
Bar,
XAxis,
YAxis,
ResponsiveContainer,
} from 'recharts'
import { api, type Alert, type ProcessMetric } from '@/lib/api'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { SystemChart } from '@/components/SystemChart'
// ── shared tooltip style ──────────────────────────────────────────────────────
const TOOLTIP_STYLE = {
contentStyle: {
background: '#111118',
border: '1px solid #00ff9d33',
borderRadius: 6,
fontSize: 12,
fontFamily: 'JetBrains Mono, monospace',
},
labelStyle: { color: '#6b7280' },
}
// ── stat card ─────────────────────────────────────────────────────────────────
function StatCard({
title,
value,
icon: Icon,
accent = '#00ff9d',
}: {
title: string
value: string | number
icon: React.ElementType
accent?: string
}) {
return (
{title}
{value}
)
}
// ── active anomalies list ─────────────────────────────────────────────────────
function ActiveAnomaliesList({ procs }: { procs: ProcessMetric[] }) {
return (
Активные угрозы
{procs.length > 0 && (
{procs.length}
)}
{procs.length === 0 ? (
✓
Аномалий не обнаружено
) : (
{procs.map((p) => (
-
{p.name}
PID {p.pid}
CPU {p.cpu_percent}%
RAM {Math.round(p.memory_mb)} МБ
{p.connections > 0 && (
{p.connections} TCP
)}
))}
)}
)
}
// ── severity donut ────────────────────────────────────────────────────────────
const SEV_META = [
{ key: 'high', label: 'Высокий', color: '#ff3366' },
{ key: 'medium', label: 'Средний', color: '#ff6b35' },
{ key: 'low', label: 'Низкий', color: '#00d4ff' },
] as const
function SeverityDonut({ alerts }: { alerts: Alert[] }) {
const counts = { high: 0, medium: 0, low: 0 }
for (const a of alerts) counts[a.severity] = (counts[a.severity] || 0) + 1
const data = SEV_META.map((m) => ({ name: m.label, value: counts[m.key], color: m.color }))
.filter((d) => d.value > 0)
const renderLegend = () => (
{data.map((d) => (
-
{d.name}
{d.value}
))}
)
return (
Алерты по серьёзности
{data.length === 0 ? (
Алертов нет
) : (
{data.map((d) => (
|
))}
[v, n]}
/>
{renderLegend()}
)}
)
}
// ── top processes bar ─────────────────────────────────────────────────────────
function TopProcessesBar({ alerts }: { alerts: Alert[] }) {
const counts: Record = {}
for (const a of alerts) {
counts[a.process_name] = (counts[a.process_name] || 0) + (a.count ?? 1)
}
const data = Object.entries(counts)
.sort(([, a], [, b]) => b - a)
.slice(0, 6)
.map(([name, value]) => ({ name, value }))
return (
Топ процессов по алертам
{data.length === 0 ? (
Нет данных
) : (
[v, 'алертов']}
/>
)}
)
}
// ── main component ────────────────────────────────────────────────────────────
export function Overview() {
const { data: stats } = useQuery({
queryKey: ['stats'],
queryFn: api.stats,
refetchInterval: 5_000,
})
const { data: sysRows } = useQuery({
queryKey: ['system-metrics'],
queryFn: () => api.systemMetrics(60),
refetchInterval: 5_000,
})
const { data: activeProcs } = useQuery({
queryKey: ['active-anomalies'],
queryFn: api.activeAnomalies,
refetchInterval: 5_000,
})
const { data: alerts } = useQuery({
queryKey: ['alerts'],
queryFn: () => api.alerts(200),
refetchInterval: 10_000,
})
const chartData = (sysRows ?? []).map((r) => ({
t: r.timestamp.slice(11, 19),
cpu: Math.round(r.cpu_percent * 10) / 10,
ram: Math.round(r.ram_percent * 10) / 10,
}))
return (
{/* Stat cards */}
{/* Active anomalies + system chart */}
Нагрузка системы — реальное время (60 точек)
{/* Severity donut + top processes */}
)
}