import { cx, useState } from "zeb/react";
import { requestJson } from "@/components/lib/http";
import Button from "@/components/ui/button";
/** Declared dependencies and whether each one resolved. */
export function dependencyStatusClass(status) {
if (status === "resolved") return "border-info text-info";
if (status === "missing" || status === "integrity_mismatch") {
return "border-warning text-warning";
}
return "border-success text-success";
}
export default function DependenciesPanel({ api, initialStatus }: any) {
const [report, setReport] = useState(initialStatus ?? { ok: true, resolved: 0, problems: 0, items: [] });
const [busy, setBusy] = useState("");
const [error, setError] = useState("");
const items = Array.isArray(report?.items) ? report.items : [];
async function run(action) {
setBusy(action);
setError("");
try {
const payload = await requestJson(api, action === "repair" ? { method: "POST", body: "{}" } : {});
if (payload?.report) setReport(payload.report);
} catch (err) {
setError(err instanceof Error ? err.message : String(err));
} finally {
setBusy("");
}
}
return (
<section className="border border-border rounded-lg bg-card overflow-hidden">
<header className="flex items-start justify-between gap-3 border-b border-border px-4 py-3">
<div className="min-w-0">
<h3 className="text-[0.83rem] font-semibold tracking-[0.01em] text-foreground">Dependency Lock</h3>
<p className="mt-1 text-[0.78rem] leading-[1.45] text-muted-foreground">
Exact RWE libraries and installed node bundles required to reproduce this project.
</p>
</div>
<span className="inline-flex items-center border border-border bg-muted px-2 py-[0.38rem] text-[0.66rem] font-mono uppercase tracking-[0.12em] text-muted-foreground">
{report?.ok ? "Resolved" : `${report?.problems ?? 0} problem(s)`}
</span>
</header>
<div className="px-4 py-4">
<div className="flex flex-wrap items-center justify-between gap-3 border-b border-border pb-3">
<div className="flex items-center gap-4 text-[0.74rem] text-muted-foreground">
<span><strong className="text-foreground">{report?.resolved ?? 0}</strong> resolved</span>
<span><strong className={report?.problems ? "text-warning" : "text-foreground"}>{report?.problems ?? 0}</strong> problems</span>
<code>{report?.lock_file ?? "zeb.lock"}</code>
</div>
<div className="flex items-center gap-2">
<Button type="button" variant="outline" disabled={Boolean(busy)} onClick={() => run("refresh")}>
{busy === "refresh" ? "Refreshing..." : "Refresh"}
</Button>
<Button type="button" disabled={Boolean(busy)} onClick={() => run("repair")}>
{busy === "repair" ? "Repairing..." : "Repair"}
</Button>
</div>
</div>
{error ? <p className="border-b border-border py-3 text-[0.74rem] text-warning">{error}</p> : null}
{items.length ? (
<div className="overflow-x-auto">
<table className="w-full border-collapse text-left text-[0.74rem]">
<thead>
<tr className="border-b border-border text-muted-foreground">
<th className="px-2 py-2 font-medium">Dependency</th>
<th className="px-2 py-2 font-medium">Family</th>
<th className="px-2 py-2 font-medium">Version</th>
<th className="px-2 py-2 font-medium">Source</th>
<th className="px-2 py-2 font-medium">Status</th>
</tr>
</thead>
<tbody>
{items.map((item, index) => (
<tr key={`${item?.family}-${item?.name}-${index}`} className="border-b border-border last:border-b-0">
<td className="min-w-[15rem] px-2 py-2.5 align-top">
<code className="text-foreground">{item?.name}</code>
<p className="mt-1 text-[0.69rem] leading-[1.4] text-muted-foreground">{item?.message}</p>
{Array.isArray(item?.definitions) && item.definitions.length ? (
<p className="mt-1 font-mono text-[0.66rem] text-muted-foreground">{item.definitions.join(", ")}</p>
) : null}
</td>
<td className="whitespace-nowrap px-2 py-2.5 align-top text-muted-foreground">{String(item?.family ?? "").replaceAll("_", " ")}</td>
<td className="whitespace-nowrap px-2 py-2.5 align-top font-mono text-foreground">{item?.version}</td>
<td className="whitespace-nowrap px-2 py-2.5 align-top text-muted-foreground">{item?.source}</td>
<td className="whitespace-nowrap px-2 py-2.5 align-top">
<span className={cx("inline-flex border px-2 py-1 font-mono text-[0.65rem]", dependencyStatusClass(item?.status))}>
{String(item?.status ?? "unknown").replaceAll("_", " ")}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
) : (
<p className="py-5 text-[0.76rem] text-muted-foreground">This project has no external runtime dependencies.</p>
)}
</div>
</section>
);
}