import { type ReactNode } from "react"; import { LaptopIcon, PlusIcon, RefreshCwIcon } from "lucide-react"; import { cn } from "../lib/cn"; import type { MachineView, MetricSample } from "../types/machines"; import { MachineCard } from "./machine-card"; import { useOpenGeniText } from "../messages/runtime"; export type MachinesDashboardProps = { machines: MachineView[]; /** The session's active sandbox id (drives the per-card "Active" marker). */ activeSandboxId?: string | null | undefined; loading?: boolean | undefined; error?: Error | null | undefined; /** Attach/swap the session's active sandbox to a machine. */ onAttach?: ((machine: MachineView) => void) | undefined; /** The sandbox id currently being attached/swapped to (disables that card). */ attachingSandboxId?: string | null | undefined; onUpdateAgent?: ((machine: MachineView) => void) | undefined; updatingEnrollmentId?: string | null | undefined; /** Short recent history per machine (keyed by sandboxId) — drives card sparklines. */ seriesByMachine?: Record | undefined; /** Open the per-machine telemetry detail (makes each card actionable). */ onOpenDetail?: ((machine: MachineView) => void) | undefined; /** Shared clock so freshness/relative times render consistently across cards. */ now?: number | undefined; /** Open the connection flow (the "Connect a machine" CTA). */ onEnroll?: (() => void) | undefined; onRefresh?: (() => void) | undefined; className?: string | undefined; }; function EmptyState({ onEnroll }: { onEnroll?: (() => void) | undefined }) { const t = useOpenGeniText(); return (

{t("machinesDashboard.noMachinesYet")}

{t("machinesDashboard.connectYourOwnComputerTo")}

{onEnroll ? ( ) : null}
); } function Header({ count, onEnroll, }: { count: number; onEnroll?: (() => void) | undefined; }): ReactNode { const t = useOpenGeniText(); return (

{t("machinesDashboard.machines")}

{count}
{onEnroll ? ( ) : null}
); } /** * The workspace Machines dashboard: the fleet of Connected Machines + the * session's managed sandbox, each with its connection-status pill, state badges, * latest metrics, and an attach/swap affordance. Renders the empty state (no * machines → connect CTA), a load error, and the populated grid. The component * is purely presentational — feed it `MachinesResponse` data via `useMachines`. */ export function MachinesDashboard({ machines, activeSandboxId, loading, error, onAttach, attachingSandboxId, onUpdateAgent, updatingEnrollmentId, seriesByMachine, onOpenDetail, now, onEnroll, onRefresh, className, }: MachinesDashboardProps) { const t = useOpenGeniText(); const isEmpty = !loading && !error && machines.length === 0; return (
{error ? (
{t("machinesDashboard.couldNotLoadMachinesMessage", { message: error.message })} {onRefresh ? ( ) : null}
) : null} {loading && machines.length === 0 ? (
{[0, 1].map((i) => (
))}
) : isEmpty ? ( ) : (
{machines.map((machine) => ( ))}
)}
); }