Replace standalone catalog editor with the unified Visual Catalog studio
CI / check (push) Failing after 26s
CI / preflight (push) Skipped
CI / deploy (push) Skipped
CI / publish-container (push) Skipped

Make /admin/catalog a full-screen catalog studio that replaces the old
listing plus separate [id]/builder-club detail pages:

- Embed CatalogManagerWorkspace on /admin/catalog with a Normal/Builder
  Club toggle, Catalog Sync status, packages (normal), Organize imports
  and a diagnostics link to /admin/studio/maintenance.
- Manage BC items directly in the studio Items tab (new BcItemsEditor,
  CRUD via existing bc actions; /api/admin/catalog/items now serves BC).
- Inline editor: add pageTextTeaser field for both catalogs and remove
  the legacy full-editor links.
- Remove the 'Open full editor' context action from the tree.
- Move catalog-items-table (dir + barrel) and catalog-translate-tab out
  of the app route into src/components/admin/catalog and update all
  importers.
- Keep /admin/catalog/[id], builder-club/[id] and /admin/catalog/maintenance
  as redirects into the new studio; consolidate maintenance panels into
  /admin/studio/maintenance and point the nav item there.
- Delete the old listing/table/tabs/forms and the standalone bc-manager.
This commit is contained in:
openhands committed 2026-09-14 17:20:52 +02:00
1 parent 1a9e1e791a
commit 1bbd809b43
36 files changed
+526 -2750

No files matched your search

@@ -0,0 +1,48 @@
"use client";
import { Image as ImageIcon } from "lucide-react";
import { getHabboCdnIconUrl, getLocalIconUrl } from "@/lib/furni/classname";
export function FurniIcon({
classname,
revision,
}: {
classname: string;
revision: number;
}) {
if (!classname) {
return (
<div className="w-10 h-10 rounded bg-muted flex items-center justify-center">
<ImageIcon className="h-4 w-4 text-muted-foreground" />
</div>
);
}
const localUrl = getLocalIconUrl(classname);
function handleError(e: React.SyntheticEvent<HTMLImageElement>) {
const img = e.target as HTMLImageElement;
const stage = img.dataset.fallback;
if (!stage && revision > 0) {
img.dataset.fallback = "cdn";
img.src = getHabboCdnIconUrl(classname, revision);
} else {
img.dataset.fallback = "none";
img.style.opacity = "0";
}
}
return (
<div className="w-10 h-10 rounded bg-muted/50 flex items-center justify-center overflow-hidden">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={localUrl}
alt={classname}
className="max-w-full max-h-full object-contain"
style={{ imageRendering: "pixelated" }}
onError={handleError}
loading="lazy"
/>
</div>
);
}