From 2d14e02a6832d1dda5bbdafc4872ca773ecfd6f8 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 5 Sep 2026 15:26:46 +0200 Subject: [PATCH] fix(catalog): remove suggestions that substitute different furniture --- src/components/admin/studio/import-review.tsx | 78 ++----------------- src/components/admin/studio/studio-client.tsx | 4 +- src/lib/furni/source-assets.ts | 24 ------ .../services/furniture-source-assets.test.ts | 33 ++------ src/lib/services/furniture-source-assets.ts | 15 +--- 5 files changed, 15 insertions(+), 139 deletions(-) diff --git a/src/components/admin/studio/import-review.tsx b/src/components/admin/studio/import-review.tsx index 1e662c730b..c82473fc16 100644 --- a/src/components/admin/studio/import-review.tsx +++ b/src/components/admin/studio/import-review.tsx @@ -19,7 +19,7 @@ import type { FurniItem } from "./studio-types"; import { useSourceAssetChecks } from "./use-source-asset-checks"; export function ImportReview({ - items: originalItems, + items, sourceId, source, translation, @@ -31,10 +31,8 @@ export function ImportReview({ source: string; translation: string; onCancel: () => void; - onConfirm: (items: FurniItem[], replaced?: boolean) => void; + onConfirm: (items: FurniItem[]) => void; }) { - const [items, setItems] = useState(originalItems); - const [replacements, setReplacements] = useState([]); const inspection = useFurnitureInspection( items.map((item) => item.classname), ); @@ -112,22 +110,6 @@ export function ImportReview({ Selection: {items.length} furniture items

- {replacements.length > 0 && ( -

- Replacements selected: {replacements.join(" · ")}{" "} - -

- )} {assets.loading && !inspection.loading && (

Checking source files… {assets.items.length} checked @@ -156,58 +138,10 @@ export function ImportReview({ Source files unavailable: {check.classname}

- This item will be skipped. Choose a reachable alternative below - to replace it in this import. + The source has no downloadable files for this furniture. It + cannot be imported until its original .nitro or source files are + available.

- {check.alternatives.length === 0 && ( -

- No verified alternative found. Upload the original .nitro from - the import page if you have it. -

- )} - {check.alternatives.map((alternative) => ( -
-
- {alternative.name} -

{alternative.classname}

-

- {alternative.type} · Revision {alternative.revision} · ID{" "} - {alternative.id} -

-
- -
- ))} ))} {inspection.loading ? ( @@ -380,7 +314,7 @@ export function ImportReview({ if (step === "review") { setStep("confirm"); setPage(0); - } else onConfirm(ready, replacements.length > 0); + } else onConfirm(ready); }} > {step === "review" diff --git a/src/components/admin/studio/studio-client.tsx b/src/components/admin/studio/studio-client.tsx index 01540b3261..05d552c1f8 100644 --- a/src/components/admin/studio/studio-client.tsx +++ b/src/components/admin/studio/studio-client.tsx @@ -2419,10 +2419,10 @@ export function StudioClient({ : "Off" } onCancel={() => setReview(null)} - onConfirm={(accepted, replaced) => { + onConfirm={(accepted) => { const single = review.single; setReview(null); - if (review.cloneAll && !replaced) + if (review.cloneAll) void cloneAllMissing(accepted.map((item) => item.classname)); else if (single && accepted[0]) void importSingle(accepted[0]); else diff --git a/src/lib/furni/source-assets.ts b/src/lib/furni/source-assets.ts index f0399bc0ee..4453c6fcfa 100644 --- a/src/lib/furni/source-assets.ts +++ b/src/lib/furni/source-assets.ts @@ -14,27 +14,3 @@ export interface SourceAssetCheck { revision: number; alternatives: SourceAssetItem[]; } -export function relatedFurniture( - item: SourceAssetItem, - candidates: SourceAssetItem[], -) { - const tail = (name: string) => - name.toLowerCase().split("_").slice(-2).join("_"); - const label = (name: string) => (name ?? "").trim().toLowerCase(); - return candidates - .filter( - (candidate) => - candidate.classname !== item.classname && - candidate.type === item.type && - ((label(item.name) && label(candidate.name) === label(item.name)) || - (item.classname.split("_").length >= 3 && - tail(candidate.classname) === tail(item.classname))), - ) - .sort( - (a, b) => - Number(label(b.name) === label(item.name)) - - Number(label(a.name) === label(item.name)) || - a.classname.localeCompare(b.classname), - ) - .slice(0, 5); -} diff --git a/src/lib/services/furniture-source-assets.test.ts b/src/lib/services/furniture-source-assets.test.ts index d2324ac68c..1cf05ed9fa 100644 --- a/src/lib/services/furniture-source-assets.test.ts +++ b/src/lib/services/furniture-source-assets.test.ts @@ -1,9 +1,6 @@ // @vitest-environment node import { afterEach, describe, expect, it, vi } from "vitest"; -import { - relatedFurniture, - type SourceAssetItem, -} from "@/lib/furni/source-assets"; +import type { SourceAssetItem } from "@/lib/furni/source-assets"; import { inspectSourceAssets, probeAsset } from "./furniture-source-assets"; const source: SourceAssetItem = { @@ -23,7 +20,7 @@ const alternative = { }; afterEach(() => vi.unstubAllGlobals()); describe("source asset preflight", () => { - it("suggests reachable alternatives for unavailable source files", async () => { + it("keeps missing furniture unchanged even when a similar asset is available", async () => { const fetchMock = vi.fn( async (url: string) => new Response(null, { status: url.includes("/73257/") ? 200 : 404 }), @@ -31,7 +28,9 @@ describe("source asset preflight", () => { vi.stubGlobal("fetch", fetchMock); const result = await inspectSourceAssets(source, [source, alternative]); expect(result.state).toBe("missing"); - expect(result.alternatives).toEqual([alternative]); + expect(result.classname).toBe(source.classname); + expect(result.alternatives).toEqual([]); + expect(fetchMock).toHaveBeenCalledTimes(1); }); it("does not describe blocked or unreachable servers as missing files", async () => { vi.stubGlobal( @@ -67,28 +66,6 @@ describe("source asset preflight", () => { ); expect(result.state).toBe("available"); }); - it("bounds suggestions and excludes different furniture types", () => { - expect( - relatedFurniture(source, [source, { ...alternative, type: "wallitem" }]), - ).toEqual([]); - expect( - relatedFurniture( - source, - Array.from({ length: 20 }, (_, id) => ({ - ...alternative, - id, - classname: `f${id}_monkeybzn_pole`, - })), - ), - ).toHaveLength(5); - }); - it("handles unnamed source entries while finding related assets", () => { - expect( - relatedFurniture(source, [ - { ...alternative, name: null as unknown as string }, - ]), - ).toHaveLength(1); - }); it("caches a probe to avoid repeating CDN requests", async () => { const fetchMock = vi.fn(async () => new Response(null, { status: 200 })); vi.stubGlobal("fetch", fetchMock); diff --git a/src/lib/services/furniture-source-assets.ts b/src/lib/services/furniture-source-assets.ts index 90482fac5b..864bc4402b 100644 --- a/src/lib/services/furniture-source-assets.ts +++ b/src/lib/services/furniture-source-assets.ts @@ -3,7 +3,6 @@ import type { SourceAssetCheck, SourceAssetItem, } from "@/lib/furni/source-assets"; -import { relatedFurniture } from "@/lib/furni/source-assets"; import type { CloneSource } from "./clone-sources"; import { browserHeaders } from "./import/core/browser-headers"; @@ -78,21 +77,11 @@ export async function inspectSourceAssets( revision: item.revision || canonical?.revision || 0, }; const state = await available(effective, source); - const alternatives: SourceAssetItem[] = []; - if (state === "missing") { - for (const candidate of relatedFurniture( - { ...effective, name: canonical?.name || item.name }, - official, - )) { - if ((await available(candidate)) === "available") - alternatives.push(candidate); - if (alternatives.length === 3) break; - } - } + return { classname: item.classname, revision: effective.revision, state, - alternatives, + alternatives: [], }; }