feat(studio): add organize-imports tool with suggestion review and approval
CI / check (push) Successful in 2m22s
CI / deploy (push) Successful in 1m34s
CI / publish-container (push) Successful in 46s

Adds a Studio 'Organize imports' dialog that groups recently imported
furniture and furniture already sitting in the auto-created import
pages into suggested catalog categories.  Each group is presented with
its suggested name, icon, and layout which can be overridden before
approval; approved groups are turned into real catalog pages in a
single atomic export run.  Offers already inside the import subtree are
moved to the new pages; brand-new furniture gets a fresh offer.

- groupSuggestedCategories: generic pure helper reusing the same
  per-item label heuristic that drives suggestCategoryName; items with
  no label land in a 'Other Furni' remainder bucket
- GET /api/admin/import/organize: returns items from the imported
  furniture tree (catalog_items JOIN items_base via page id set from
  the imported-furniture root) union audit-logged but not-yet-placed
  recent imports; marks alreadyPlaced vs new
- organizeImportFurni server action: validates import-page membership
  before moving any offer, creates pages + inserts/moves items in one
  withCatalogExport snapshot, logs activity
- OrganizeImportsDialog: full-featured Studio dialog with price panel
  (applies to new offers only), parent select, per-group approval,
  editable name/icon/layout with suggestion reset chips, source tags
- import-pages.ts server helper: locates the imported-furniture tree
- Studio nav: 'Organize imports' button with FolderTree icon,
  gated on CATALOG_EDIT, wired next to the catalog manager
- en + nl translations for organizeImports.* keys with ICU plurals
- groupSuggestedCategories unit tests (deterministic grouping,
  remainder handling, size+alpha ordering, label consistency)
This commit is contained in:
openhands committed 2026-09-11 19:38:18 +02:00
1 parent f5c2c05f6e
commit 01a85ebcd0
9 files changed
+1230 -15

No files matched your search

+24 -5
View File
@@ -7,6 +7,7 @@ import {
ClipboardCheck,
Copy,
Dumbbell,
FolderTree,
Image as ImageIcon,
type LucideIcon,
Puzzle,
@@ -18,6 +19,7 @@ import {
import { usePathname } from "next/navigation";
import { useState } from "react";
import { CatalogManagerDialog } from "@/app/admin/catalog/catalog-manager-dialog-wrapper";
import { OrganizeImportsDialog } from "@/components/admin/studio/organize-imports-dialog";
import Link from "@/components/link";
import {
Popover,
@@ -113,6 +115,7 @@ export function StudioLayoutClient({
children: React.ReactNode;
}) {
const pathname = usePathname() ?? "";
const [organizeOpen, setOrganizeOpen] = useState(false);
return (
<div className="flex h-[calc(100dvh-7rem)] min-h-[600px] min-w-0 flex-col overflow-hidden rounded-xl border border-[var(--admin-border)] bg-[var(--admin-canvas)] text-[var(--admin-text)]">
<nav
@@ -144,11 +147,21 @@ export function StudioLayoutClient({
</div>
<div className="flex flex-wrap gap-1">
{canEditCatalog && (
<CatalogManagerDialog
catalogType="normal"
canEdit
triggerLabel="Catalog manager"
/>
<>
<CatalogManagerDialog
catalogType="normal"
canEdit
triggerLabel="Catalog manager"
/>
<button
type="button"
onClick={() => setOrganizeOpen(true)}
className="flex min-h-10 items-center gap-2 rounded-lg px-3 text-sm font-medium text-[var(--admin-text-muted)] hover:bg-[var(--admin-accent)]/10"
>
<FolderTree size={16} />
Organize imports
</button>
</>
)}
<StudioMenu
label="Import & sync"
@@ -163,6 +176,12 @@ export function StudioLayoutClient({
</div>
</nav>
<div className="min-h-0 min-w-0 flex-1 overflow-auto">{children}</div>
{canEditCatalog && (
<OrganizeImportsDialog
open={organizeOpen}
onOpenChange={setOrganizeOpen}
/>
)}
</div>
);
}