import { useEffect, useState } from 'react'; import { apiFetch } from '@/lib/api'; import { formatTimeAgo } from '@/lib/relativeTime'; import { SystemSheet, SheetSection } from '@/components/ui/system-sheet'; import { Badge } from '@/components/ui/badge'; import { Loader2, Activity, Hash } from 'lucide-react'; import type { MeshRouteDiagnostic, MeshActivityEvent, MeshProbeResult } from '@/types/mesh'; import { meshRouteStateFromBackend, meshRouteStateTokens } from './meshRouteState'; interface Props { open: boolean; onOpenChange: (open: boolean) => void; alias: string | null; } type RouteTab = 'overview' | 'events' | 'raw'; export function MeshRouteDetailSheet({ open, onOpenChange, alias }: Props) { const [diag, setDiag] = useState(null); const [events, setEvents] = useState([]); const [probe, setProbe] = useState(null); const [probing, setProbing] = useState(false); const [loading, setLoading] = useState(false); const [activeTab, setActiveTab] = useState('overview'); useEffect(() => { if (!open || !alias) return; let cancelled = false; const refresh = async () => { setLoading(true); try { const [diagRes, evRes] = await Promise.all([ apiFetch(`/mesh/aliases/${encodeURIComponent(alias)}/diagnostic`, { localOnly: true }), apiFetch(`/mesh/activity?alias=${encodeURIComponent(alias)}&limit=20`, { localOnly: true }), ]); if (cancelled) return; if (diagRes.ok) setDiag(await diagRes.json()); if (evRes.ok) { const body = await evRes.json() as { events: MeshActivityEvent[] }; setEvents(body.events); } } finally { if (!cancelled) setLoading(false); } }; void refresh(); return () => { cancelled = true; }; }, [open, alias]); const runProbe = async () => { if (!alias) return; setProbing(true); setProbe(null); try { const res = await apiFetch(`/mesh/aliases/${encodeURIComponent(alias)}/test`, { method: 'POST', localOnly: true, }); const body = await res.json() as MeshProbeResult; setProbe(body); } finally { setProbing(false); } }; if (!alias) return null; const pillState = diag ? meshRouteStateFromBackend(diag.state) : 'not-authorized'; const pill = meshRouteStateTokens(pillState); const meta = diag?.target ? `${diag.target.stack}/${diag.target.service}:${diag.target.port} · node #${diag.target.nodeId}` : (loading ? 'Loading…' : 'No target resolved'); const footerContext = probe ? (probe.ok ? `Last probe ok · ${probe.latencyMs}ms` : `Last probe failed · ${probe.where ?? 'unknown'}`) : diag?.lastProbeMs != null ? (diag.lastProbeAt != null ? `Last probe ${formatTimeAgo(diag.lastProbeAt)} · ${diag.lastProbeMs}ms` : `Last probe ${diag.lastProbeMs}ms`) : 'No probe run yet'; return ( { void runProbe(); }, disabled: probing, }} tabs={[ { id: 'overview', label: 'Overview' }, { id: 'events', label: 'Events', count: events.length }, { id: 'raw', label: 'Raw' }, ]} activeTab={activeTab} onTabChange={(id) => setActiveTab(id as RouteTab)} footerContext={footerContext} size="md" > {activeTab === 'overview' && ( <>
{pill.label} {probe && ( {probe.ok ? `ok ${probe.latencyMs}ms` : `${probe.where ?? 'fail'}: ${probe.code ?? 'error'}`} )}
{diag?.target && (
Target node
#{diag.target.nodeId}
Stack / service
{diag.target.stack}/{diag.target.service}
Port
{diag.target.port}
Pilot tunnel
{diag.pilot.connected ? 'connected' : 'disconnected'}
)} {diag?.lastError && (
{diag.lastError.message}
{new Date(diag.lastError.ts).toLocaleString()}
)} )} {activeTab === 'events' && (
{loading && } {!loading && events.length === 0 && (
No events yet for this alias.
)} {events.map((e, i) => (
{e.source === 'pilot' && } {e.source === 'mesh' && } {new Date(e.ts).toLocaleTimeString()} {e.type} {e.message}
))}
)} {activeTab === 'raw' && (
                        {diag ? JSON.stringify(diag, null, 2) : 'No diagnostic data loaded.'}
                    
)}
); }