feat(catalog): repair missing furniture components with verified status
CI / check (push) Successful in 1m33s
CI / deploy (push) Successful in 1m22s
CI / e2e (push) Successful in 24s

This commit is contained in:
Simo committed 2026-09-05 18:12:34 +02:00
1 parent 3c24df9b1d
commit 570f3dde44
13 files changed
+438 -164

No files matched your search

@@ -5,6 +5,7 @@ import { adminFetch } from "@/lib/admin-fetch";
import {
compareFurniture,
type FurnitureInspection,
furnitureHealth,
} from "@/lib/furni/studio-inspection";
import type { FurniItem } from "./studio-types";
@@ -172,7 +173,15 @@ export function FurnitureComparison({
</div>
);
}
export function FurnitureInspector({ item }: { item: FurniItem }) {
export function FurnitureInspector({
item,
onRepair,
busy = false,
}: {
item: FurniItem;
onRepair?: () => Promise<unknown>;
busy?: boolean;
}) {
const inspection = useFurnitureInspection([item.classname]);
if (inspection.loading)
return (
@@ -192,8 +201,54 @@ export function FurnitureInspector({ item }: { item: FurniItem }) {
const local = inspection.items.find(
(entry) => entry.classname === item.classname,
);
return local ? (
<FurnitureComparison item={item} local={local} />
const health = local ? furnitureHealth(local) : null;
return local && health ? (
<div className="space-y-3">
<section className="rounded-md border border-[var(--admin-border)] p-3 space-y-2">
<strong>
{
{
complete: "Complete",
incomplete: "Needs repair",
new: "Not imported",
conflict: "Local conflict",
unknown: "Not verified",
}[health.state]
}
</strong>
{health.missing.length > 0 && (
<p className="text-sm">Missing: {health.missing.join(", ")}</p>
)}
{health.state === "conflict" && (
<p className="text-sm">
Duplicate records or mismatched IDs/types require review before
repair.
</p>
)}
{health.state === "unknown" && (
<p className="text-sm">
Some local data could not be checked. Retry verification before
repair.
</p>
)}
{health.state === "incomplete" && onRepair && (
<>
<p className="text-sm text-[var(--admin-text-muted)]">
Repair adds missing parts and preserves existing IDs, settings,
furnidata and catalog offers. Missing offers use the automatic
category and price shown below.
</p>
<Button disabled={busy} onClick={() => void onRepair()}>
{busy ? "Repair queued or running…" : "Repair furni"}
</Button>
</>
)}
<Button variant="outline" disabled={busy} onClick={inspection.retry}>
Recheck local state
</Button>
</section>
<FurnitureComparison item={item} local={local} />
</div>
) : (
<p>Local inspection unavailable.</p>
);