"use client"; import { Suspense, useEffect, useState } from "react"; import { useSearchParams, useRouter } from "next/navigation"; import { useOrchestratorStatus } from "@/hooks/use-agents"; import { useTasks } from "@/hooks/use-tasks"; import { usePageRefresh } from "@/hooks"; import { useUsageSummary, useUsageTimeSeries, useAgentUsage, useTeamUsage, useModelUsage, useRoleUsage, useUsageProjection, useCacheEfficiency, useSpawnWaste, useUsageSessions, } from "@/hooks/use-usage"; import type { UsagePeriod } from "@/lib/api/usage"; import { TaskStatus, Team } from "@/types"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Progress } from "@/components/ui/progress"; import { Skeleton } from "@/components/ui/skeleton"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { SegmentedControl } from "@/components/ui/segmented-control"; import { OfflineState } from "@/components/ui/offline-state"; import { ResponsiveTable, ResponsiveTableCardList, ResponsiveTableCard, ResponsiveTableCardRow, } from "@/components/ui/responsive-table"; import { DeliveryTabContent } from "@/components/metrics/delivery-tab"; import { ScorecardsTabContent } from "@/components/metrics/scorecards-tab"; import { UsageTimeSeriesChart, ModelUsageDonut, AgentUsageChart, TeamUsageChart, TaskStatusChart, TaskStatusTiles, SessionsTable, } from "@/components/metrics"; import { Activity, TrendingUp, TrendingDown, Clock, AlertTriangle, Users, CheckCircle, XCircle, Zap, Timer, Coins, Sparkles, } from "lucide-react"; import type { UsageProjection as UP, CacheEfficiencyResponse as CER, RoleUsageRow, SpawnWasteResponse, } from "@/types"; // ─── Humanized number formatting ───────────────────────────────────────────── /** Format counts with K/M suffix for values >= 1000. */ function humanizeCount(n: number): string { if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + "M"; if (n >= 1_000) return (n / 1_000).toFixed(1) + "K"; return String(n); } /** Format token counts (same as humanizeCount but used for token display). */ function fmtTokens(n: number): string { if (n >= 1_000_000) return (n / 1_000_000).toFixed(2) + "M"; if (n >= 1_000) return (n / 1_000).toFixed(1) + "K"; return String(n); } // ─── Shared sub-components ──────────────────────────────────────────────────── interface MetricCardProps { title: string; value: string | number; subtitle?: string; icon: React.ReactNode; trend?: "up" | "down" | "neutral"; trendValue?: string; } function MetricCard({ title, value, subtitle, icon, trend, trendValue, }: MetricCardProps) { const displayValue = typeof value === "number" ? humanizeCount(value) : value; return ( {title} {icon}
{displayValue}
{subtitle && (

{subtitle}

)} {trend && trendValue && (
{trendValue}
)}
); } interface TeamHealthCardProps { team: Team; activeTasks: number; blockedTasks: number; completedToday: number; } function TeamHealthCard({ team, activeTasks, blockedTasks, completedToday, }: TeamHealthCardProps) { const healthScore = Math.max(0, 100 - blockedTasks * 20); return ( {team.replace(/_/g, " ")} Cell
{healthScore}%
{activeTasks}
Active
{blockedTasks}
Blocked
{completedToday}
Done
); } // ─── Performance tab content ───────────────────────────────────────────────── function PerformanceTabContent() { const { data: tasks, error: tasksError, refetch: refetchTasks } = useTasks(); const { data: status, error: statusError, refetch: refetchStatus, } = useOrchestratorStatus(); const { register, unregister, refresh } = usePageRefresh(); useEffect(() => { const callbacks = [ () => { void refetchTasks(); }, () => { void refetchStatus(); }, ]; callbacks.forEach((cb) => register(cb)); return () => { callbacks.forEach((cb) => unregister(cb)); }; }, [register, unregister, refetchTasks, refetchStatus]); const isOffline = (tasksError || statusError) && (tasksError?.message?.includes("Network Error") || statusError?.message?.includes("Network Error")); const taskList = tasks || []; const agentList = status?.agents || []; // Velocity metrics const completedToday = taskList.filter((t) => { if (!t.completed_at) return false; const completed = new Date(t.completed_at); const today = new Date(); return completed.toDateString() === today.toDateString(); }).length; const completedThisWeek = taskList.filter((t) => { if (!t.completed_at) return false; const completed = new Date(t.completed_at); const weekAgo = new Date(); weekAgo.setDate(weekAgo.getDate() - 7); return completed > weekAgo; }).length; // Task status counts const pending = taskList.filter( (t) => t.status === TaskStatus.PENDING, ).length; const inProgress = taskList.filter( (t) => t.status === TaskStatus.IN_PROGRESS, ).length; const blocked = taskList.filter( (t) => t.status === TaskStatus.BLOCKED, ).length; const awaitingQa = taskList.filter( (t) => t.status === TaskStatus.AWAITING_QA, ).length; const completed = taskList.filter( (t) => t.status === TaskStatus.COMPLETED, ).length; // Agent counts const runningAgents = status?.by_state?.active || agentList.filter((a) => a.state === "active").length; const idleAgents = status?.by_state?.idle || agentList.filter((a) => a.state === "idle" || a.state === "offline").length; const waitingAgents = status?.waiting_count || agentList.filter((a) => a.state === "waiting_long").length; const errorAgents = status?.by_state?.error || agentList.filter((a) => a.state === "error").length; // Team metrics const teamMetrics = Object.values(Team).map((team) => { const teamTasks = taskList.filter((t) => t.team === team); return { team, activeTasks: teamTasks.filter((t) => [TaskStatus.IN_PROGRESS, TaskStatus.CLAIMED].includes(t.status), ).length, blockedTasks: teamTasks.filter((t) => t.status === TaskStatus.BLOCKED) .length, completedToday: teamTasks.filter((t) => { if (!t.completed_at) return false; const c = new Date(t.completed_at); const today = new Date(); return c.toDateString() === today.toDateString(); }).length, }; }); if (isOffline) { return ( void refresh()} /> ); } return (
{/* Velocity Metrics */}

Velocity

} /> } /> } /> 0 ? Math.round((completed / taskList.length) * 100) + "%" : "0%" } subtitle="Of all tasks" icon={} />
{/* Task Status */}

Task Status

, }, { label: "In Progress", value: inProgress, icon: , }, { label: "Blocked", value: blocked, icon: , }, { label: "Awaiting QA", value: awaitingQa, icon: , }, { label: "Completed", value: completed, icon: , }, ]} />
{/* Agent Status */}

Agent Status

} /> } /> } /> } />
{/* Team Health */}

Team Health

{teamMetrics.map((tm) => ( ))}
); } // ─── Token Usage & Costs tab content ───────────────────────────────────────── const TIME_WINDOW_OPTIONS: { value: UsagePeriod; label: string }[] = [ { value: "24h", label: "24h" }, { value: "7d", label: "7d" }, { value: "30d", label: "30d" }, { value: "90d", label: "90d" }, ]; function TokenUsageCostsSection() { const [period, setPeriod] = useState("24h"); const { data: summary, isLoading: loadingSnap } = useUsageSummary(period); const { data: timeSeries, isLoading: loadingTS } = useUsageTimeSeries(period); const { data: agentUsage, isLoading: loadingAgents } = useAgentUsage(period); const { data: teamUsage, isLoading: loadingTeams } = useTeamUsage(period); const { data: sessions, isLoading: loadingSessions } = useUsageSessions(100); const { data: modelUsage, isLoading: loadingModels } = useModelUsage(period); const { data: projection, isLoading: loadingProj } = useUsageProjection(); const { data: cacheStats, isLoading: loadingCache } = useCacheEfficiency(period); const { data: roleUsage, isLoading: loadingRoles } = useRoleUsage(period); const { data: waste, isLoading: loadingWaste } = useSpawnWaste(period); const trendUp = (summary?.trend_pct ?? 0) >= 0; return (
{/* Time window selector — drives every period-scoped hook below */}
setPeriod(v as UsagePeriod)} aria-label="Usage time window" />
{/* Row 1 — Summary cards */}
} isLoading={loadingSnap} /> } isLoading={loadingSnap} /> } isLoading={loadingSnap} /> ) : ( ) } isLoading={loadingSnap} /> } isLoading={loadingSnap} /> } isLoading={loadingCache} />
{/* Row 2 — Time series + model donut */}
{/* Row 3 — Agent bar + team bar */}
{/* Row 4 — Projection + cache efficiency */}
{/* Row 5 — Per-role cost/cache + spawn waste */}
{/* Row 6 — Sessions table */}
); } // ─── Helper sub-components ──────────────────────────────────────────────────── interface SummaryCardProps { title: string; value: string | undefined; icon: React.ReactNode; trend?: { dir: "up" | "down"; label: string }; isLoading: boolean; } function SummaryCard({ title, value, icon, trend, isLoading, }: SummaryCardProps) { return ( {title} {icon} {isLoading ? ( ) : ( <>
{value ?? "—"}
{trend && (

{trend.label}

)} )}
); } interface ProjectionCardProps { projection: UP | undefined; isLoading: boolean; } function ProjectionCard({ projection, isLoading }: ProjectionCardProps) { return ( Monthly Projection {isLoading ? ( ) : (
{projection != null ? "$" + projection.projected_monthly_cost_usd.toFixed(2) : "—"}

Based on {projection?.basis_days ?? 7}-day rolling average ($ {projection?.avg_daily_cost_usd.toFixed(4) ?? "—"}/day)

)}
); } interface CacheEfficiencyCardProps { cacheStats: CER | undefined; isLoading: boolean; } function CacheEfficiencyCard({ cacheStats, isLoading, }: CacheEfficiencyCardProps) { const pct = cacheStats ? cacheStats.cache_hit_rate * 100 : 0; return ( Cache Efficiency {isLoading ? ( ) : (
{pct.toFixed(1)}%

{cacheStats ? fmtTokens(cacheStats.tokens_cache_read) : "—"} cache reads · saved $ {cacheStats?.cost_saved_by_cache_usd.toFixed(4) ?? "—"}

)}
); } interface RoleUsageTableProps { data: RoleUsageRow[] | undefined; isLoading: boolean; } function RoleUsageTable({ data, isLoading }: RoleUsageTableProps) { return ( Cost & Cache by Role {isLoading ? ( ) : !data || data.length === 0 ? (

No usage recorded yet.

) : ( Role Cost Cache hit % {data.map((r) => ( {r.role} ${r.cost_usd.toFixed(4)} {(r.cache_hit_rate * 100).toFixed(1)}% {r.pct_of_total.toFixed(1)}% ))} } cards={ {data.map((r) => ( {r.role}
${r.cost_usd.toFixed(4)} {(r.cache_hit_rate * 100).toFixed(1)}% {r.pct_of_total.toFixed(1)}%
))}
} /> )}
); } interface SpawnWasteCardProps { data: SpawnWasteResponse | undefined; isLoading: boolean; } function SpawnWasteCard({ data, isLoading }: SpawnWasteCardProps) { return ( Spawn Waste {isLoading ? ( ) : !data ? (

No spawn data yet.

) : (
{data.unproductive_pct.toFixed(1)}%

{data.unproductive_spawns} of {data.total_spawns} spawns produced no output

{data.by_role.length > 0 && ( {data.by_role.map((r) => ( {r.role} {r.unproductive}/{r.spawns} {r.unproductive_pct.toFixed(0)}% ))} } cards={
{data.by_role.map((r) => (
{r.role} {r.unproductive}/{r.spawns} ( {r.unproductive_pct.toFixed(0)}%)
))}
} /> )} {data.respawn_strikes.length > 0 && (

{data.respawn_strikes.length} wedged task {data.respawn_strikes.length === 1 ? "" : "s"} with open respawn strikes

)}
)}
); } // ─── Tab types ──────────────────────────────────────────────────────────────── type MetricsTab = "performance" | "token-usage" | "delivery" | "scorecards"; const VALID_METRICS_TABS: MetricsTab[] = [ "performance", "token-usage", "delivery", "scorecards", ]; function isValidMetricsTab(value: string | null): value is MetricsTab { return VALID_METRICS_TABS.includes(value as MetricsTab); } // ─── Main page content (uses useSearchParams) ───────────────────────────────── function MetricsPageContent() { const router = useRouter(); const searchParams = useSearchParams(); // Read ?tab= from URL, default to "performance" const rawTab = searchParams.get("tab"); const activeTab: MetricsTab = isValidMetricsTab(rawTab) ? rawTab : "performance"; function handleTabChange(value: string) { const params = new URLSearchParams(searchParams.toString()); params.set("tab", value); router.push(`?${params.toString()}`); } return (
{/* Header */}

Metrics

Performance analytics and operational insights

Performance Token Usage Delivery Scorecards
); } // Wrap in Suspense for useSearchParams export default function MetricsPage() { return (
{Array.from({ length: 4 }).map((_, i) => ( ))}
} >
); }