Сжать экран «Сеть»: KPI (stats-12) и вкладки Пиры/Спикеры через ResourcePage; убраны дубли charts и заглушка control-plane. Co-authored-by: Cursor <[email protected]>
116 lines
3.3 KiB
TypeScript
116 lines
3.3 KiB
TypeScript
import { AlertTriangle, Network, ServerCog, Share2 } from 'lucide-react'
|
|
|
|
import { Badge } from '@/components/reui/badge'
|
|
import { KpiStatGrid, type KpiStatCardData } from '@/components/reui-kit'
|
|
import { aggregateNetworkMetrics } from '@/queries/overview'
|
|
import type { BirdStatus, PeerRow, SpeakerRow } from '@/types/api'
|
|
|
|
/**
|
|
* Network page KPI strip.
|
|
* @see https://reui.io/preview/base/stats-12
|
|
*/
|
|
export function NetworkKpi({
|
|
peers,
|
|
speakers,
|
|
bird,
|
|
loading,
|
|
}: {
|
|
peers: PeerRow[]
|
|
speakers: SpeakerRow[]
|
|
bird?: BirdStatus
|
|
loading?: boolean
|
|
}) {
|
|
const net = aggregateNetworkMetrics(peers, speakers)
|
|
const birdHealthy = bird?.healthy
|
|
const birdSessions =
|
|
bird != null ? `${bird.bgp_established} / ${bird.bgp_sessions_total}` : '—'
|
|
|
|
const cards: KpiStatCardData[] = [
|
|
{
|
|
id: 'peers-established',
|
|
icon: <Share2 aria-hidden />,
|
|
iconClassName: 'text-success',
|
|
label: 'Пиры Established',
|
|
value: loading ? '—' : `${net.peersEstablished}/${net.peersEnabled}`,
|
|
footer: (
|
|
<Badge variant="success-light" size="sm">
|
|
{loading ? '…' : `${net.peersTotal} в каталоге`}
|
|
</Badge>
|
|
),
|
|
to: '/network',
|
|
search: { tab: 'peers' },
|
|
},
|
|
{
|
|
id: 'speakers-online',
|
|
icon: <ServerCog aria-hidden />,
|
|
iconClassName: 'text-info',
|
|
label: 'Спикеры online',
|
|
value: loading ? '—' : `${net.speakersOnline}/${net.speakersTotal}`,
|
|
footer: (
|
|
<Badge
|
|
variant={
|
|
net.speakersOnline === net.speakersTotal && net.speakersTotal > 0
|
|
? 'success-light'
|
|
: 'warning-light'
|
|
}
|
|
size="sm"
|
|
>
|
|
{loading ? '…' : 'live'}
|
|
</Badge>
|
|
),
|
|
to: '/network',
|
|
search: { tab: 'speakers' },
|
|
},
|
|
{
|
|
id: 'mismatches',
|
|
icon: <AlertTriangle aria-hidden />,
|
|
iconClassName: net.peersMismatch > 0 ? 'text-warning' : 'text-muted-foreground',
|
|
label: 'Расхождения',
|
|
value: loading ? '—' : String(net.peersMismatch),
|
|
variant: net.peersMismatch > 0 ? 'warning' : 'default',
|
|
footer: (
|
|
<Badge variant={net.peersMismatch > 0 ? 'warning-light' : 'outline'} size="sm">
|
|
{loading ? '…' : net.peersMismatch > 0 ? 'проверить' : 'в норме'}
|
|
</Badge>
|
|
),
|
|
to: '/network',
|
|
search: { tab: 'peers' },
|
|
},
|
|
{
|
|
id: 'bird',
|
|
icon: <Network aria-hidden />,
|
|
iconClassName:
|
|
birdHealthy === false ? 'text-destructive' : 'text-primary',
|
|
label: 'BIRD',
|
|
value: loading ? '—' : birdSessions,
|
|
variant: birdHealthy === false ? 'destructive' : 'default',
|
|
footer: (
|
|
<Badge
|
|
variant={
|
|
birdHealthy === true
|
|
? 'success-light'
|
|
: birdHealthy === false
|
|
? 'destructive-light'
|
|
: 'outline'
|
|
}
|
|
size="sm"
|
|
>
|
|
{loading
|
|
? '…'
|
|
: birdHealthy === true
|
|
? 'в норме'
|
|
: birdHealthy === false
|
|
? 'проблема'
|
|
: 'н/д'}
|
|
</Badge>
|
|
),
|
|
},
|
|
]
|
|
|
|
return (
|
|
<section aria-label="Ключевые метрики сети">
|
|
<KpiStatGrid cards={cards} isLoading={loading} skeletonCount={4} />
|
|
</section>
|
|
)
|
|
}
|