import { Link, useNavigate } from "@tanstack/react-router"; import { motion } from "framer-motion"; import { Boxes, LayoutDashboard, Network, Settings as SettingsIcon } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; import { computeFleetAggregates, DEFAULT_ROW_METRICS, type FleetRowMetrics, } from "@/components/fleet/fleetAggregates"; import { EmptyState } from "@/components/shared/EmptyState"; import { Body, PageTitle, SectionHeading } from "@/components/ui/typography"; import { useInstances } from "@/hooks/useInstances"; import type { Instance } from "@/lib/config"; import { COLOR } from "@/lib/constants"; import { formatCount } from "@/lib/utils"; import { ServerWorkspaceRows } from "./ServerWorkspaceRows"; const ALL_SERVERS = "all"; /** * Unified, server-aware dashboard: every workspace across every configured server, * labelled ` ()` and filterable by server. Aggregates fold in the * cross-server totals (formerly the standalone Fleet view). Opening a workspace * activates its server, then drills into the existing workspace detail route. */ export function Dashboard() { const { instances, activeId, activate } = useInstances(); const navigate = useNavigate(); const [serverFilter, setServerFilter] = useState(ALL_SERVERS); const [metricsById, setMetricsById] = useState>({}); useEffect(() => { if (serverFilter !== ALL_SERVERS && !instances.find((i) => i.id === serverFilter)) { setServerFilter(ALL_SERVERS); } }, [instances, serverFilter]); const onMetrics = useCallback((id: string, m: FleetRowMetrics) => { setMetricsById((prev) => ({ ...prev, [id]: m })); }, []); const onOpenWorkspace = useCallback( (instance: Instance, workspaceId: string) => { if (instance.id !== activeId) activate(instance.id); navigate({ to: "/workspaces/$workspaceId", params: { workspaceId } as never }); }, [activeId, activate, navigate], ); const shownInstances = useMemo( () => serverFilter === ALL_SERVERS ? instances : instances.filter((i) => i.id === serverFilter), [instances, serverFilter], ); const agg = useMemo( () => computeFleetAggregates(shownInstances.map((i) => metricsById[i.id] ?? DEFAULT_ROW_METRICS)), [shownInstances, metricsById], ); if (instances.length === 0) { return (
Go to Settings } />
); } return (
Dashboard {agg.totalInstances} server{agg.totalInstances !== 1 ? "s" : ""}
Workspaces across every configured server
0 ? COLOR.destructive : "var(--text-3)"} />
Workspaces {instances.length > 1 && ( )}
{shownInstances.map((inst) => ( ))}
Workspace (server) Conclusions Queue (a/p)
); } interface MetricCardProps { label: string; value: number; total?: number; color?: string; accent?: boolean; } function MetricCard({ label, value, total, color, accent }: MetricCardProps) { const valueColor = color ?? (accent ? COLOR.accentText : "var(--text-1)"); return (
{formatCount(value)} {total !== undefined && ( / {formatCount(total)} )}
{label}
); }