import { type ReactNode, useState } from "react"; import { Link } from "react-router"; import type { ApiError, Job } from "../api"; import { useJob } from "../hooks"; export function Loading({ what = "Loading" }: { what?: string }) { return
{what}…
; } export function ErrorBox({ error }: { error: ApiError | Error | string | null }) { if (!error) return null; const e = error as ApiError; return
{typeof error === "string" ? error : `${e.code ? e.code + ": " : ""}${e.message}`}{e.correlationId ? (ref {e.correlationId}) : null}
; } export function Empty({ children }: { children: ReactNode }) { return
{children}
; } const TONE: Record = { succeeded: "good", done: "good", pass: "good", verified: "good", allow: "good", online: "good", ok: "good", resolved: "good", approved: "good", validated: "good", registered: "good", monitored: "good", tested: "good", independently_reproduced: "good", production: "good", failed: "critical", dead: "critical", fail: "critical", blocked: "critical", deny: "critical", critical: "critical", revoked: "critical", rejected: "critical", block: "critical", high: "serious", serious: "serious", quarantined: "serious", stale: "serious", deprecated: "serious", quarantine: "serious", unverified: "serious", research: "accent", medium: "warn", warn: "warn", warning: "warn", running: "warn", in_progress: "warn", intent: "warn", unobserved: "warn", unknown: "warn", discovery: "warn", open: "warn", low: "", info: "", queued: "", proposed: "", observed: "", documented: "", implemented: "accent", inspected: "", modeled: "", discovered: "", development: "", accepted: "", }; export function Badge({ v, label }: { v: string | null | undefined; label?: string }) { const s = v ?? "—"; return {label ?? s}; } export function Bool({ v, yes = "yes", no = "no" }: { v: boolean | null | undefined; yes?: string; no?: string }) { return {v ? yes : no}; } export function Td({ l, children, className, wide }: { l: string; children: ReactNode; className?: string; wide?: boolean }) { return {children}; } export function KV({ items }: { items: [string, ReactNode][] }) { return
{items.map(([k, v]) => (
{k}
{v ?? "—"}
))}
; } export function Tabs({ tabs, active, onChange }: { tabs: string[]; active: string; onChange: (t: string) => void }) { return
{tabs.map((t) => )}
; } export function JobStatus({ jobId, label = "job", onDone }: { jobId: string | null; label?: string; onDone?: (j: Job) => void }) { const job = useJob(jobId, onDone); if (!jobId) return null; if (!job) return
Submitted {label} {jobId.slice(0, 8)}… waiting for worker
; const terminal = ["done", "dead", "cancelled", "failed"].includes(job.state); return (
{label} {job.id.slice(0, 8)} {!terminal && (attempt {job.attempts}, polling…)}details
{job.error &&
{job.error}
} {job.result && }
); } export function JsonTree({ data, open = false, depth = 0 }: { data: unknown; open?: boolean; depth?: number }) { if (data === null || data === undefined) return null; if (typeof data === "string") return "{data.length > 400 ? data.slice(0, 400) + "…" : data}"; if (typeof data === "number" || typeof data === "boolean") return {String(data)}; const isArr = Array.isArray(data); const entries = isArr ? (data as unknown[]).map((v, i) => [String(i), v] as [string, unknown]) : Object.entries(data as Record); if (entries.length === 0) return {isArr ? "[]" : "{}"}; const inner = ( ); const wrapper =
{isArr ? `array[${entries.length}]` : `object{${entries.length}}`}{inner}
; return depth === 0 ?
{wrapper}
: wrapper; } export function CopyBox({ text }: { text: string }) { const [ok, setOk] = useState(false); return (
{text}
); } export function Confirm({ label, onConfirm, className = "btn sm danger", disabled }: { label: string; onConfirm: () => void; className?: string; disabled?: boolean }) { const [arm, setArm] = useState(false); if (!arm) return ; return ; } export function Pager({ offset, limit, total, onChange }: { offset: number; limit: number; total: number; onChange: (o: number) => void }) { return
{total === 0 ? "0" : `${offset + 1}–${Math.min(offset + limit, total)}`} of {total}
; } export function Spinner() { return working…; }