// ---------------------------------------------------------------------------- // MachineDetail — the marquee per-machine telemetry view. // // Opens from a machine card. Answers, at a glance and then in depth: is this // machine healthy right now (one fused verdict), what are its live numbers, and // how have they moved over the chosen window. Built entirely on the existing // `og-*` token system — no new visual language, just the missing depth. // ---------------------------------------------------------------------------- import { ArrowLeftIcon, BuildingIcon, ChevronRightIcon, CpuIcon, LaptopIcon, PlugIcon, RadioIcon, ServerIcon, TriangleAlertIcon, Trash2Icon, UserIcon, UsersIcon, } from "lucide-react"; import { cn } from "../../lib/cn"; import { formatRelativeTime } from "../../lib/format"; import type { MachineView, MetricSample } from "../../types/machines"; import { connectionStatusForState } from "../../types/machines"; import { AgentStatusIcon } from "./agent-status-icon"; import { deriveHealth, HEALTH_TOKEN, healthPulses } from "./health"; import { describeAgentConnection, describeAgentRuntime, runnerConflict, STATUS_TONE_TEXT, } from "./machine-status"; import { MetricHistoryChart } from "./metric-history-chart"; import { MetricSparkline } from "./metric-sparkline"; import { METRICS, METRIC_WINDOWS, pointsFor, WINDOW_LABEL, type MetricWindow } from "./series"; import { MachineOperationPolicyEditor } from "./machine-operation-policy"; import type { UpdateMachineOperationPolicyRequest } from "@opengeni/sdk"; import { useOpenGeniText, type OpenGeniText } from "../../messages/runtime"; export type MachineDetailProps = { machine: MachineView; /** Downsampled history for the active window (from `fetchSeries`). */ series: MetricSample[]; window: MetricWindow; onWindowChange: (w: MetricWindow) => void; loadingSeries?: boolean | undefined; onBack?: (() => void) | undefined; /** Open the workspace-admin removal confirmation for a self-hosted machine. */ onRemove?: ((machine: MachineView) => void) | undefined; onUpdateOperationPolicy?: | ((machine: MachineView, request: UpdateMachineOperationPolicyRequest) => Promise) | undefined; updatingOperationPolicy?: boolean | undefined; /** Open "Who can use it?" for a connected machine. Omit when the viewer * can't change it; the row then shows the current audience read-only. */ onChangeAudience?: ((machine: MachineView) => void) | undefined; /** Start, retry or resend the signed agent update. Omit when the viewer can't. */ onUpdateAgent?: ((machine: MachineView) => void) | undefined; updatingAgent?: boolean | undefined; now?: number | undefined; className?: string | undefined; }; const AUDIENCE = ( t: OpenGeniText, ): Record => ({ user: { label: t("machines.machineDetail.onlyMe"), description: t("machines.machineDetail.onlyItsOwnerCanRun"), Icon: UserIcon, }, workspace: { label: t("machines.machineDetail.thisWorkspace"), description: t("machines.machineDetail.everyoneInThisWorkspaceCan"), Icon: UsersIcon, }, organization: { label: t("machines.machineDetail.wholeOrganization"), description: t("machines.machineDetail.everyWorkspaceInTheOrganization"), Icon: BuildingIcon, }, }); /** Who can use a connected machine. A navigational row when it can change. */ function MachineAudienceRow({ machine, onChange, }: { machine: MachineView; onChange?: ((machine: MachineView) => void) | undefined; }) { const t = useOpenGeniText(); const audience = AUDIENCE(t)[machine.scope]; const changeable = onChange !== undefined && machine.scope !== "organization"; const content = ( <> {t("machines.machineDetail.whoCanUseIt")} {audience.description} {audience.label} {changeable ? ( ) : null} ); const frame = "flex w-full min-h-14 items-center gap-3 rounded-og-lg border border-og-border bg-og-surface-1 px-4 py-3 shadow-og-sm"; return changeable ? ( ) : (
{content}
); } function KindIcon({ kind, session }: { kind: string; session: boolean }) { if (kind === "local" || kind === "selfhosted") return ; if (kind === "docker" || session) return ; if (kind === "modal") return ; return ; } /** * The connected agent: its build and update on one row, its connection on the * next. Past events read as quiet history; only a live problem gets a color. */ function MachineAgentPanel({ machine, now, onUpdateAgent, updatingAgent, }: { machine: MachineView; now: number; onUpdateAgent?: ((machine: MachineView) => void) | undefined; updatingAgent?: boolean | undefined; }) { const t = useOpenGeniText(); const authority = machine.connectionAuthority; const runtime = machine.runtime; if (!runtime && authority.state === "not_applicable") return null; const agent = runtime ? describeAgentRuntime(t, runtime, now, connectionStatusForState(machine.state) === "online") : null; const action = agent?.action && machine.enrollmentId && onUpdateAgent && machine.state !== "offline" ? agent.action : null; const connection = authority.state === "not_applicable" ? null : describeAgentConnection(t, authority); const conflict = runnerConflict(authority, now); const conflictAgo = conflict?.lastAt ? formatRelativeTime(t, conflict.lastAt, new Date(now)) : null; return (
{agent ? (
{agent.title} {agent.status} {agent.note ? {agent.note} : null}
{action ? ( ) : null}
) : null} {connection ? (
{connection.label} {connection.detail ? ( {connection.detail} ) : null} {conflict?.active ? ( {t("machines.machineDetail.aSecondCopyOfThe")} ) : conflict ? ( {t("machines.machineDetail.keptOutExtraCopiesOf", { count: conflict.count })} {conflictAgo ? t("machines.machineDetail.mostRecentlyValue", { value: conflictAgo === "now" ? t("machines.machineDetail.justNow") : `${conflictAgo} ago`, }) : ""} ) : null}
) : null}
); } export function MachineDetail({ machine, series, window, onWindowChange, loadingSeries, onBack, onRemove, onUpdateOperationPolicy, updatingOperationPolicy, onChangeAudience, onUpdateAgent, updatingAgent, now = Date.now(), className, }: MachineDetailProps) { const t = useOpenGeniText(); const health = deriveHealth(t, machine.state, machine.metrics, now); const tone = HEALTH_TOKEN[health.level]; const latest = machine.metrics; const sampledAgo = latest ? formatRelativeTime(t, latest.sampledAt, new Date(now)) : null; // Only render metrics that have a value (GPU is dropped on headless boxes). const visible = METRICS(t).filter((m) => !(m.key === "gpu" && latest?.gpuUtilPct == null)); // The tile grid tracks the metric count exactly so it never leaves a dead cell. const tileCols = visible.length >= 5 ? "grid-cols-2 sm:grid-cols-3 lg:grid-cols-5" : "grid-cols-2 sm:grid-cols-4"; return (
{/* ── top bar ─────────────────────────────────────────────────────── */}
{onBack && ( )}

{machine.name}

{machine.os} · {machine.arch}
{onRemove && machine.enrollmentId && !machine.isSessionGroup ? ( ) : null} {/* range selector */}
{METRIC_WINDOWS.map((w) => ( ))}
{/* ── health hero ─────────────────────────────────────────────────── */}
{healthPulses(health.level) && ( )}
{health.label} {health.reason}
{sampledAgo && (
{t("machines.machineDetail.sampled")} {sampledAgo === "now" ? t("machines.machineDetail.live") : t("machines.machineDetail.sampledagoAgo", { sampledAgo })}
)} {machine.lastSeenAt && (
{t("machines.machineDetail.lastSeen")} {formatRelativeTime(t, machine.lastSeenAt, new Date(now))}
)}
{/* current-stat tiles */} {latest && (
{visible.map((m) => { const pts = pointsFor(m, series); return (
{m.title} {m.current(latest)} {m.sub && ( {m.sub(latest)} )}
); })}
)}
{machine.enrollmentId && !machine.isSessionGroup ? ( ) : null} {onUpdateOperationPolicy && machine.enrollmentId && machine.operationPolicy ? ( onUpdateOperationPolicy(machine, request)} /> ) : null} {/* ── history charts ──────────────────────────────────────────────── */}
{visible.map((m) => { const pts = pointsFor(m, series); return (
{m.title} {latest && ( {m.current(latest)} )}
); })}
); }