"use client"; import { useState, useEffect, useCallback } from "react"; import { motion } from "framer-motion"; import { Activity, Database, Cpu, Wifi, Shield, RefreshCw, TrendingUp, AlertTriangle, CheckCircle, Clock } from "lucide-react"; interface Metrics { uptime_seconds: number; requests: { total: number; errors: number; auth_failures: number; error_rate_pct: number }; websocket: { total_connects: number; reconnects: number }; ai: { fallbacks: number; by_provider: Record; }; cache: { hits: number; misses: number; hit_ratio_pct: number }; route_timings: Record; recent_errors: Array<{ ts: string; path: string; status: number; detail: string }>; } interface ApiStatus { ai_backend: string; ai_available: boolean; db_type: string; cache_type: string; version: string; } const API = process.env.NEXT_PUBLIC_API_URL || ""; function formatUptime(s: number) { if (s < 60) return `${s}s`; if (s < 3600) return `${Math.floor(s / 60)}m ${Math.floor(s % 60)}s`; return `${Math.floor(s / 3600)}h ${Math.floor((s % 3600) / 60)}m`; } function StatusDot({ ok }: { ok: boolean }) { return ( ); } function MetricCard({ label, value, sub, icon: Icon, color = "text-zinc-400" }: { label: string; value: string | number; sub?: string; icon: React.ElementType; color?: string; }) { return (

{label}

{value}

{sub &&

{sub}

}
); } const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.06 } }, }; const itemVariants = { hidden: { opacity: 0, y: 14 }, visible: { opacity: 1, y: 0, transition: { duration: 0.4 } }, }; export default function StatusPage() { const [metrics, setMetrics] = useState(null); const [status, setStatus] = useState(null); const [loading, setLoading] = useState(true); const [lastRefresh, setLastRefresh] = useState(null); const [backendOnline, setBackendOnline] = useState(false); const load = useCallback(async () => { setLoading(true); try { const [m, s] = await Promise.all([ fetch(`${API}/api/metrics`).then((r) => r.json()), fetch(`${API}/api/status`).then((r) => r.json()), ]); setMetrics(m); setStatus(s); setBackendOnline(true); setLastRefresh(new Date()); } catch { setBackendOnline(false); } finally { setLoading(false); } }, []); useEffect(() => { load(); const interval = setInterval(load, 15000); // auto-refresh every 15s return () => clearInterval(interval); }, [load]); const aiProviders = metrics ? Object.entries(metrics.ai.by_provider) : []; const topRoutes = metrics ? Object.entries(metrics.route_timings).sort((a, b) => b[1].avg_ms - a[1].avg_ms).slice(0, 6) : []; return ( {/* Header */}

System Status

Live observability — auto-refreshes every 15s {lastRefresh && ( · Last updated {lastRefresh.toLocaleTimeString()} )}

{/* Overall health banner */} {backendOnline ? ( ) : ( )}

{backendOnline ? "All Systems Operational" : "Backend Offline"}

{backendOnline ? `API v${status?.version || "2.0.0"} · ${status?.db_type?.toUpperCase()} · ${status?.cache_type?.toUpperCase()} cache · Uptime ${formatUptime(metrics?.uptime_seconds || 0)}` : "Cannot reach backend. Start with: uvicorn app.main:app --port 8000"}

{/* System info cards */} {status && ( {[ { label: "AI Backend", value: status.ai_backend.toUpperCase(), sub: status.ai_available ? "Available" : "Unavailable", icon: Cpu, color: status.ai_available ? "text-emerald-400" : "text-red-400", }, { label: "Database", value: status.db_type.toUpperCase(), sub: "Connected", icon: Database, color: "text-blue-400", }, { label: "Cache", value: status.cache_type.toUpperCase(), sub: metrics ? `${metrics.cache.hit_ratio_pct}% hit rate` : "—", icon: Activity, color: "text-purple-400", }, { label: "Uptime", value: formatUptime(metrics?.uptime_seconds || 0), sub: "Since last restart", icon: Clock, color: "text-amber-400", }, ].map((c) => ( ))} )} {/* Request metrics */} {metrics && ( 5 ? "text-red-400" : "text-emerald-400"} /> 10 ? "text-amber-400" : "text-zinc-400"} /> )}
{/* AI Provider Health */}

AI Provider Health

{aiProviders.length === 0 ? (

No AI calls recorded yet

Use the chat or briefing endpoints to generate AI calls

) : (
{aiProviders.map(([provider, data]) => { const errorRate = data.calls > 0 ? (data.errors / data.calls) * 100 : 0; return (
{provider}
{data.calls} calls 10 ? "text-red-400" : "text-emerald-400"}> {errorRate.toFixed(1)}% errors {data.avg_latency_ms}ms avg
); })}

AI fallbacks: {metrics?.ai.fallbacks || 0}

)} {/* Cache Performance */}

Cache Performance

{metrics && (
Hit Ratio 60 ? "text-emerald-400" : "text-amber-400"}`}> {metrics.cache.hit_ratio_pct}%

{metrics.cache.hits}

Cache Hits

{metrics.cache.misses}

Cache Misses

)}
{/* Route Timings */} {topRoutes.length > 0 && (

Route Performance

Sorted by avg response time
{topRoutes.map(([path, data]) => { const isSlow = data.avg_ms > 500; const barWidth = Math.min((data.avg_ms / 2000) * 100, 100); return (
{path}
{data.avg_ms}ms avg {data.max_ms}ms max {data.calls} calls
); })}
)} {/* Recent Errors */} {metrics && metrics.recent_errors.length > 0 && (

Recent Errors

Last 10
{metrics.recent_errors.map((err, i) => (
= 500 ? "bg-red-500/20 text-red-400" : "bg-amber-500/20 text-amber-400" }`}> {err.status} {err.path} {new Date(err.ts).toLocaleTimeString()}
))}
)} ); }