import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { PieChart, Pie, Cell } from 'recharts'; import { Activity, CheckCircle2, XCircle, Loader2 } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Progress } from '@/components/ui/progress'; import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from '@/components/ui/chart'; import { EmptyState } from '@/components/ui/empty-state'; import type { ScanStatus } from '@/lib/api'; interface StatsPanelProps { status: ScanStatus | null; } const CHART_COLORS = { hosts: 'hsl(var(--chart-1))', ports: 'hsl(var(--chart-2))', services: 'hsl(var(--chart-3))', vulns: 'hsl(var(--chart-4))', } as const; export function StatsPanel({ status }: StatsPanelProps) { const { t } = useTranslation(); const isRunning = status?.state === 'running'; const isStopping = status?.state === 'stopping'; const statsChartData = useMemo(() => { if (!status?.stats) return []; return [ { name: t('statsHosts'), value: status.stats.hosts_scanned || 0, fill: CHART_COLORS.hosts }, { name: t('statsPorts'), value: status.stats.ports_scanned || 0, fill: CHART_COLORS.ports }, { name: t('statsServices'), value: status.stats.services_found || 0, fill: CHART_COLORS.services }, { name: t('statsVulns'), value: status.stats.vulns_found || 0, fill: CHART_COLORS.vulns }, ].filter(d => d.value > 0); }, [status?.stats, t]); const chartConfig: ChartConfig = { hosts: { label: t('statsHosts'), color: CHART_COLORS.hosts }, ports: { label: t('statsPorts'), color: CHART_COLORS.ports }, services: { label: t('statsServices'), color: CHART_COLORS.services }, vulns: { label: t('statsVulns'), color: CHART_COLORS.vulns }, }; const totalStats = useMemo(() => { if (!status?.stats) return 0; return (status.stats.hosts_scanned || 0) + (status.stats.ports_scanned || 0) + (status.stats.services_found || 0) + (status.stats.vulns_found || 0); }, [status?.stats]); const statsItems = [ { key: 'hosts', label: t('statsHosts'), value: status?.stats.hosts_scanned || 0, color: CHART_COLORS.hosts }, { key: 'ports', label: t('statsPorts'), value: status?.stats.ports_scanned || 0, color: CHART_COLORS.ports }, { key: 'services', label: t('statsServices'), value: status?.stats.services_found || 0, color: CHART_COLORS.services }, { key: 'vulns', label: t('statsVulns'), value: status?.stats.vulns_found || 0, color: CHART_COLORS.vulns }, ]; return ( {t('resultsDistribution')} {isRunning ? : isStopping ? : } {isRunning ? t('scanRunning') : isStopping ? t('statusStopping') : t('statusIdle')} {/* Progress */} {isRunning && (
{t('loading')} {status?.progress || 0}%
)} {/* Pie Chart */} {totalStats > 0 ? (
{statsChartData.map((entry, index) => ( ))} } />
) : ( )} {/* Stats List */}
{statsItems.map((item) => (
{item.label}
{item.value}
))}
{/* Total */}
{t('items')} {totalStats}
); }