Surface post-import verification results in the Studio
After a batch import the progress panel now shows a verification summary (offer_id fixed, furnidata ids fixed / missing / conflicts, nitros and icons synced, folders chowned). Single imports include the key counts in the success toast.
This commit is contained in:
1 parent
b4968a9967
commit
635abfbc9f
3 files changed
+105
-3
No files matched your search
@@ -1,10 +1,36 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Ban, Check, Loader2, X } from "lucide-react";
|
import { Ban, Check, Loader2, ShieldCheck, X } from "lucide-react";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import type { BatchItemStatus } from "./studio-types";
|
import type { BatchItemStatus, VerificationSummary } from "./studio-types";
|
||||||
|
|
||||||
|
function SummaryRow({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
tone,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
value: string | number;
|
||||||
|
tone?: "ok" | "warn";
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between gap-2 text-xs">
|
||||||
|
<span className="text-[var(--admin-text-muted)]">{label}</span>
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"font-mono font-semibold",
|
||||||
|
tone === "warn"
|
||||||
|
? "text-[var(--admin-warning)]"
|
||||||
|
: "text-[var(--admin-success)]",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function BatchProgress({
|
export function BatchProgress({
|
||||||
progress,
|
progress,
|
||||||
@@ -12,6 +38,7 @@ export function BatchProgress({
|
|||||||
succeeded,
|
succeeded,
|
||||||
failed,
|
failed,
|
||||||
cancelling,
|
cancelling,
|
||||||
|
verification,
|
||||||
onCancel,
|
onCancel,
|
||||||
onDismiss,
|
onDismiss,
|
||||||
}: {
|
}: {
|
||||||
@@ -20,6 +47,7 @@ export function BatchProgress({
|
|||||||
succeeded: number;
|
succeeded: number;
|
||||||
failed: number;
|
failed: number;
|
||||||
cancelling?: boolean;
|
cancelling?: boolean;
|
||||||
|
verification?: VerificationSummary | null;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
onDismiss?: () => void;
|
onDismiss?: () => void;
|
||||||
}) {
|
}) {
|
||||||
@@ -96,6 +124,45 @@ export function BatchProgress({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{done && verification && (
|
||||||
|
<div className="mt-2 space-y-1.5 rounded-lg border border-[var(--admin-border)] bg-[var(--admin-canvas)] p-2.5">
|
||||||
|
<div className="flex items-center gap-1.5 text-xs font-bold uppercase tracking-wider text-[var(--admin-text-muted)]">
|
||||||
|
<ShieldCheck size={13} className="text-[var(--admin-success)]" />
|
||||||
|
Post-import verification
|
||||||
|
</div>
|
||||||
|
<SummaryRow
|
||||||
|
label="Catalog offer_id fixed"
|
||||||
|
value={verification.offerIdsFixed}
|
||||||
|
/>
|
||||||
|
<SummaryRow
|
||||||
|
label="Furnidata ids fixed"
|
||||||
|
value={verification.furniDataFixedIds}
|
||||||
|
/>
|
||||||
|
<SummaryRow
|
||||||
|
label="Furnidata entries missing"
|
||||||
|
value={verification.furniDataMissing}
|
||||||
|
tone={verification.furniDataMissing > 0 ? "warn" : "ok"}
|
||||||
|
/>
|
||||||
|
<SummaryRow
|
||||||
|
label="Furnidata id conflicts"
|
||||||
|
value={verification.furniDataConflicts}
|
||||||
|
tone={verification.furniDataConflicts > 0 ? "warn" : "ok"}
|
||||||
|
/>
|
||||||
|
<SummaryRow
|
||||||
|
label="Nitros synced"
|
||||||
|
value={verification.nitrosSynced.length}
|
||||||
|
/>
|
||||||
|
<SummaryRow
|
||||||
|
label="Icons synced"
|
||||||
|
value={verification.iconsSynced.length}
|
||||||
|
/>
|
||||||
|
<SummaryRow
|
||||||
|
label="Folders chowned"
|
||||||
|
value={verification.ownershipFixed.length}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="mt-2 flex justify-end gap-2">
|
<div className="mt-2 flex justify-end gap-2">
|
||||||
{done ? (
|
{done ? (
|
||||||
<Button size="sm" variant="secondary" onClick={onDismiss}>
|
<Button size="sm" variant="secondary" onClick={onDismiss}>
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ import type {
|
|||||||
FurniItem,
|
FurniItem,
|
||||||
FurniStats,
|
FurniStats,
|
||||||
Meta,
|
Meta,
|
||||||
|
VerificationSummary,
|
||||||
} from "./studio-types";
|
} from "./studio-types";
|
||||||
|
|
||||||
export function StudioClient({
|
export function StudioClient({
|
||||||
@@ -119,6 +120,9 @@ export function StudioClient({
|
|||||||
const [batchSucceeded, setBatchSucceeded] = useState(0);
|
const [batchSucceeded, setBatchSucceeded] = useState(0);
|
||||||
const [batchFailed, setBatchFailed] = useState(0);
|
const [batchFailed, setBatchFailed] = useState(0);
|
||||||
const [batchCancelling, setBatchCancelling] = useState(false);
|
const [batchCancelling, setBatchCancelling] = useState(false);
|
||||||
|
const [verification, setVerification] = useState<VerificationSummary | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
const batchAbortRef = useRef<AbortController | null>(null);
|
const batchAbortRef = useRef<AbortController | null>(null);
|
||||||
|
|
||||||
// Nitro editor
|
// Nitro editor
|
||||||
@@ -363,7 +367,16 @@ export function StudioClient({
|
|||||||
setDetail({ ...item, alreadyImported: true, nitroExists: true });
|
setDetail({ ...item, alreadyImported: true, nitroExists: true });
|
||||||
}
|
}
|
||||||
fetchStats();
|
fetchStats();
|
||||||
toast.success(`${item.classname} imported into the catalog`);
|
const parts = [`${item.classname} imported into the catalog`];
|
||||||
|
if (typeof data.furniDataFixedIds === "number") {
|
||||||
|
parts.push(
|
||||||
|
`verified: ${data.offerIdsFixed} offer_id, ${data.furniDataFixedIds} furnidata id`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (data.furniDataMissing > 0) {
|
||||||
|
parts.push(`${data.furniDataMissing} furnidata missing`);
|
||||||
|
}
|
||||||
|
toast.success(parts.join(" · "));
|
||||||
} else {
|
} else {
|
||||||
toast.error(data.error || `Import failed for ${item.classname}`);
|
toast.error(data.error || `Import failed for ${item.classname}`);
|
||||||
}
|
}
|
||||||
@@ -462,6 +475,15 @@ export function StudioClient({
|
|||||||
} else if (event.type === "batch_complete") {
|
} else if (event.type === "batch_complete") {
|
||||||
setBatchSucceeded(event.succeeded);
|
setBatchSucceeded(event.succeeded);
|
||||||
setBatchFailed(event.failed);
|
setBatchFailed(event.failed);
|
||||||
|
setVerification({
|
||||||
|
offerIdsFixed: event.offerIdsFixed ?? 0,
|
||||||
|
furniDataFixedIds: event.furniDataFixedIds ?? 0,
|
||||||
|
furniDataMissing: event.furniDataMissing ?? 0,
|
||||||
|
furniDataConflicts: event.furniDataConflicts ?? 0,
|
||||||
|
nitrosSynced: event.nitrosSynced ?? [],
|
||||||
|
iconsSynced: event.iconsSynced ?? [],
|
||||||
|
ownershipFixed: event.ownershipFixed ?? [],
|
||||||
|
});
|
||||||
setBatchDone(true);
|
setBatchDone(true);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -1173,10 +1195,12 @@ export function StudioClient({
|
|||||||
succeeded={batchSucceeded}
|
succeeded={batchSucceeded}
|
||||||
failed={batchFailed}
|
failed={batchFailed}
|
||||||
cancelling={batchCancelling}
|
cancelling={batchCancelling}
|
||||||
|
verification={verification}
|
||||||
onCancel={cancelBatch}
|
onCancel={cancelBatch}
|
||||||
onDismiss={() => {
|
onDismiss={() => {
|
||||||
setBatchProgress(null);
|
setBatchProgress(null);
|
||||||
setBatchDone(false);
|
setBatchDone(false);
|
||||||
|
setVerification(null);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -40,3 +40,14 @@ export interface BatchItemStatus {
|
|||||||
warnings?: string[];
|
warnings?: string[];
|
||||||
itemId?: number;
|
itemId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Post-import verification results reported by the import API. */
|
||||||
|
export interface VerificationSummary {
|
||||||
|
offerIdsFixed: number;
|
||||||
|
furniDataFixedIds: number;
|
||||||
|
furniDataMissing: number;
|
||||||
|
furniDataConflicts: number;
|
||||||
|
nitrosSynced: string[];
|
||||||
|
iconsSynced: string[];
|
||||||
|
ownershipFixed: string[];
|
||||||
|
}
|
||||||
Reference in new issue
Block a user