feat(catalog): add auto-category wizard with live preview and smart suggestions
CI / check (push) Successful in 3m1s
CI / deploy (push) Successful in 2m7s
CI / publish-container (push) Successful in 45s

- Add AutoCategoryDialog: pick furni (or empty page), suggest caption/icon/layout, live preview
- Add createAutoCategory server action (page + offers in one export) with CatalogKind
- Extend furni search API with interactionType
- Share ShopTile and refactor inline-editor/items-shop-preview to use it
- Add suggestion heuristics (suggestCategoryName/dIcon/layout) with tests
- Add autoCategory translations (en/nl)
This commit is contained in:
openhands committed 2026-09-11 18:47:31 +02:00
1 parent baeb54aeb5
commit f832479e52
14 files changed
+2577 -567

No files matched your search

@@ -1,6 +1,6 @@
"use client";
import { LayoutGrid, Search, X } from "lucide-react";
import { LayoutGrid, Search, Wand2, X } from "lucide-react";
import { useTranslations } from "next-intl";
import { useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
@@ -15,6 +15,7 @@ import { useLatestRequest } from "@/features/catalog/client/use-latest-request";
import { CatalogSearch } from "@/features/catalog/components/catalog-search";
import type { CatalogSearchResult } from "@/features/catalog/domain/search";
import type { RootTab } from "@/types/catalog";
import { AutoCategoryDialog } from "./auto-category-dialog";
import { BreadcrumbBar } from "./breadcrumb-bar";
import { InlineEditor, type PageUpdateData } from "./inline-editor";
import { RootTabs } from "./root-tabs";
@@ -165,6 +166,7 @@ function CatalogManagerInner({
const rootRequests = useLatestRequest();
const searchNavigation = useLatestRequest();
const [searchOpen, setSearchOpen] = useState(false);
const [autoOpen, setAutoOpen] = useState(false);
const [searchTarget, setSearchTarget] = useState<{
offerId: number | null;
key: number;
@@ -399,6 +401,23 @@ function CatalogManagerInner({
{heading}
</h2>
<div className="flex items-center gap-1">
{canEdit && catalogType === "normal" && (
<Button
variant="outline"
size="sm"
aria-label={t("autoCategory.title")}
title={t("autoCategory.title")}
onClick={() => {
if (!mayLeave()) return;
setAutoOpen(true);
}}
>
<Wand2 className="size-4" />
<span className="hidden sm:inline">
{t("autoCategory.title")}
</span>
</Button>
)}
<Button
variant="outline"
size="sm"
@@ -437,6 +456,16 @@ function CatalogManagerInner({
</div>
)}
<AutoCategoryDialog
catalogType={catalogType}
open={autoOpen}
onOpenChange={setAutoOpen}
onCreated={(pageId) => {
void loadRootPages();
handleSelectPage(pageId);
}}
/>
<BreadcrumbBar onSelectPage={handleSelectPage} />
<RootTabs