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
+517 -2741

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
-44
View File
@@ -1,44 +0,0 @@
import { Skeleton } from "@/components/ui/skeleton";
export default function CatalogDetailLoading() {
return (
<div className="space-y-6">
{/* Breadcrumb */}
<div className="flex items-center gap-2">
<Skeleton className="h-4 w-16" />
<Skeleton className="h-4 w-4" />
<Skeleton className="h-4 w-24" />
<Skeleton className="h-4 w-4" />
<Skeleton className="h-4 w-20" />
</div>
{/* Title */}
<div>
<Skeleton className="h-9 w-64" />
<Skeleton className="h-5 w-40 mt-2" />
</div>
{/* Tabs */}
<div className="space-y-4">
<div className="flex gap-2 border-b pb-2">
{Array.from({ length: 4 }, (_, i) => i).map((n) => (
<Skeleton
key={`skeleton-tab-${n}`}
className="h-9 w-24 rounded-md"
/>
))}
</div>
{/* Tab content */}
<div className="rounded-lg border bg-card p-6 space-y-4">
{Array.from({ length: 6 }, (_, i) => i).map((n) => (
<div key={`skeleton-field-${n}`} className="space-y-2">
<Skeleton className="h-4 w-24" />
<Skeleton className="h-10 w-full" />
</div>
))}
</div>
</div>
</div>
);
}
File diff suppressed because it is too large. Load diff
@@ -1,8 +1,23 @@
import { asc, eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { notFound, redirect } from "next/navigation"; import { notFound, redirect } from "next/navigation";
import { CatalogItemsBc, CatalogPagesBc, db } from "@/lib/db"; import { CatalogPagesBc, db } from "@/lib/db";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
import { BcPageDetail } from "../bc-manager";
async function topAncestorId(parentId: number): Promise<number | null> {
let current = parentId;
for (let i = 0; i < 50; i++) {
if (current <= 0) return null;
const [page] = await db
.select({ id: CatalogPagesBc.id, parentId: CatalogPagesBc.parentId })
.from(CatalogPagesBc)
.where(eq(CatalogPagesBc.id, current))
.limit(1);
if (!page) return null;
if (page.parentId <= 0) return page.id;
current = page.parentId;
}
return null;
}
export default async function BcPageDetailPage({ export default async function BcPageDetailPage({
params: paramsPromise, params: paramsPromise,
@@ -18,50 +33,13 @@ export default async function BcPageDetailPage({
redirect("/admin"); redirect("/admin");
} }
const canEdit = canAccess(permissions, PERMS.CATALOG_EDIT, session.user.rank);
const [bcPage] = await db const [bcPage] = await db
.select() .select({ id: CatalogPagesBc.id, parentId: CatalogPagesBc.parentId })
.from(CatalogPagesBc) .from(CatalogPagesBc)
.where(eq(CatalogPagesBc.id, id)) .where(eq(CatalogPagesBc.id, id))
.limit(1); .limit(1);
if (!bcPage) notFound(); if (!bcPage) notFound();
const bcItems = await db const rootId = (await topAncestorId(bcPage.parentId)) ?? id;
.select() redirect(`/admin/catalog?catalog=bc&root=${rootId}&category=${id}`);
.from(CatalogItemsBc)
.where(eq(CatalogItemsBc.pageId, id))
.orderBy(asc(CatalogItemsBc.orderNumber));
return (
<BcPageDetail
page={{
id: bcPage.id,
parentId: bcPage.parentId,
caption: bcPage.caption,
pageLayout: bcPage.pageLayout,
iconColor: bcPage.iconColor,
iconImage: bcPage.iconImage,
orderNum: bcPage.orderNum,
visible: bcPage.visible,
enabled: bcPage.enabled,
pageHeadline: bcPage.pageHeadline,
pageTeaser: bcPage.pageTeaser,
pageSpecial: bcPage.pageSpecial ?? "",
pageText1: bcPage.pageText1 ?? "",
pageText2: bcPage.pageText2 ?? "",
pageTextDetails: bcPage.pageTextDetails ?? "",
pageTextTeaser: bcPage.pageTextTeaser ?? "",
}}
items={bcItems.map((i) => ({
id: i.id,
pageId: i.pageId,
itemIds: i.itemIds,
catalogName: i.catalogName,
orderNumber: i.orderNumber,
extradata: i.extradata,
}))}
canEdit={canEdit}
/>
);
} }
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1 -26
View File
@@ -1,7 +1,4 @@
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { getTranslations } from "next-intl/server";
import { CatalogIntegrityPanel } from "@/components/admin/catalog/catalog-integrity-panel";
import { CatalogMaintenancePanel } from "@/components/admin/catalog/catalog-maintenance-panel";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
export default async function CatalogMaintenancePage() { export default async function CatalogMaintenancePage() {
@@ -9,27 +6,5 @@ export default async function CatalogMaintenancePage() {
if (!canAccess(permissions, PERMS.CATALOG_VIEW, session.user.rank)) { if (!canAccess(permissions, PERMS.CATALOG_VIEW, session.user.rank)) {
redirect("/admin"); redirect("/admin");
} }
redirect("/admin/studio/maintenance");
const canRepair = canAccess(
permissions,
PERMS.CATALOG_EDIT,
session.user.rank,
);
const t = await getTranslations("pages.admin.catalog.integrity");
return (
<div className="space-y-6">
<CatalogIntegrityPanel canRepair={canRepair} />
{canRepair && (
<details className="rounded-xl border p-4">
<summary className="cursor-pointer font-medium">
{t("legacyTitle")}
</summary>
<p className="mt-3 text-sm text-muted-foreground">
{t("legacyHint")}
</p>
<CatalogMaintenancePanel />
</details>
)}
</div>
);
} }
File diff suppressed because it is too large. Load diff
+20 -1
View File
@@ -1,4 +1,6 @@
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { getTranslations } from "next-intl/server";
import { CatalogIntegrityPanel } from "@/components/admin/catalog/catalog-integrity-panel";
import { CatalogMaintenancePanel } from "@/components/admin/catalog/catalog-maintenance-panel"; import { CatalogMaintenancePanel } from "@/components/admin/catalog/catalog-maintenance-panel";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
@@ -8,9 +10,26 @@ export default async function StudioCatalogMaintenancePage() {
redirect("/admin"); redirect("/admin");
} }
const canRepair = canAccess(
permissions,
PERMS.CATALOG_EDIT,
session.user.rank,
);
const t = await getTranslations("pages.admin.catalog.integrity");
return ( return (
<div className="min-h-0"> <div className="min-h-0 space-y-6">
<CatalogIntegrityPanel canRepair={canRepair} />
{canRepair && (
<details className="rounded-xl border p-4">
<summary className="cursor-pointer font-medium">
{t("legacyTitle")}
</summary>
<p className="mt-3 text-sm text-muted-foreground">
{t("legacyHint")}
</p>
<CatalogMaintenancePanel /> <CatalogMaintenancePanel />
</details>
)}
</div> </div>
); );
} }
+11
View File
@@ -1,5 +1,7 @@
import { asc, eq } from "drizzle-orm";
import { apiError, apiOk } from "@/lib/api"; import { apiError, apiOk } from "@/lib/api";
import { withAdmin } from "@/lib/api-handler"; import { withAdmin } from "@/lib/api-handler";
import { CatalogItemsBc, db } from "@/lib/db";
import { PERMS } from "@/lib/permissions"; import { PERMS } from "@/lib/permissions";
import { loadCatalogItemsData } from "@/lib/services/catalog-items-loader"; import { loadCatalogItemsData } from "@/lib/services/catalog-items-loader";
@@ -16,6 +18,15 @@ export const GET = withAdmin(
if (!Number.isFinite(pageId) || pageId <= 0) { if (!Number.isFinite(pageId) || pageId <= 0) {
return apiError("Invalid pageId"); return apiError("Invalid pageId");
} }
const isBc = request.nextUrl.searchParams.get("catalog") === "bc";
if (isBc) {
const items = await db
.select()
.from(CatalogItemsBc)
.where(eq(CatalogItemsBc.pageId, pageId))
.orderBy(asc(CatalogItemsBc.orderNumber));
return apiOk({ items: jsonSafe(items) });
}
const data = await loadCatalogItemsData(pageId); const data = await loadCatalogItemsData(pageId);
return apiOk(jsonSafe(data) as unknown as Record<string, unknown>); return apiOk(jsonSafe(data) as unknown as Record<string, unknown>);
}, },
@@ -5,7 +5,7 @@ import { useTranslations } from "next-intl";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { type CatalogKind, createAutoCategory } from "@/actions/catalog"; import { type CatalogKind, createAutoCategory } from "@/actions/catalog";
import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table/types"; import type { CatalogItemData } from "@/components/admin/catalog/catalog-items-table/types";
import { IconPicker } from "@/components/admin/catalog/icon-picker"; import { IconPicker } from "@/components/admin/catalog/icon-picker";
import { useCatalogManager } from "@/components/admin/catalog-manager/use-catalog-store"; import { useCatalogManager } from "@/components/admin/catalog-manager/use-catalog-store";
import { CatalogIcon, LAYOUT_COLORS } from "@/components/admin/catalog-tree"; import { CatalogIcon, LAYOUT_COLORS } from "@/components/admin/catalog-tree";
@@ -4,7 +4,7 @@ import { ArrowDownUp, Loader2, Wand2 } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { reorderCatalogItems } from "@/actions/catalog-items"; import { reorderCatalogItems } from "@/actions/catalog-items";
import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table/types"; import type { CatalogItemData } from "@/components/admin/catalog/catalog-items-table/types";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Select, Select,
File diff suppressed because it is too large. Load diff
@@ -47,7 +47,7 @@ export function CatalogManagerWorkspace({
return ( return (
<section <section
aria-label={t("manager")} aria-label={t("manager")}
className="flex h-[calc(100dvh-12rem)] min-h-[32rem] min-w-0 flex-col overflow-hidden rounded-lg border border-[var(--admin-border)] bg-[var(--admin-canvas)] text-[var(--admin-text)]" className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden rounded-lg border border-[var(--admin-border)] bg-[var(--admin-canvas)] text-[var(--admin-text)]"
> >
<CatalogManagerProvider <CatalogManagerProvider
key={catalogType} key={catalogType}
@@ -4,7 +4,6 @@ import {
AlertCircle, AlertCircle,
ChevronDown, ChevronDown,
ChevronRight, ChevronRight,
ExternalLink,
Languages, Languages,
Loader2, Loader2,
MonitorPlay, MonitorPlay,
@@ -20,16 +19,16 @@ import { updateBcPage } from "@/actions/catalog-bc";
import { import {
type CatalogItemData, type CatalogItemData,
CatalogItemsTable, CatalogItemsTable,
} from "@/app/admin/catalog/[id]/catalog-items-table"; } from "@/components/admin/catalog/catalog-items-table";
import { CatalogTranslateTab } from "@/app/admin/catalog/[id]/catalog-translate-tab"; import { CatalogTranslateTab } from "@/components/admin/catalog/catalog-translate-tab";
import { BulkImportItems } from "@/components/admin/catalog/bulk-import-items"; import { BulkImportItems } from "@/components/admin/catalog/bulk-import-items";
import { CatalogImagePicker } from "@/components/admin/catalog/catalog-image-picker"; import { CatalogImagePicker } from "@/components/admin/catalog/catalog-image-picker";
import { IconPicker } from "@/components/admin/catalog/icon-picker"; import { IconPicker } from "@/components/admin/catalog/icon-picker";
import { QuickAddFurni } from "@/components/admin/catalog/quick-add-furni"; import { QuickAddFurni } from "@/components/admin/catalog/quick-add-furni";
import { AutoOrganize } from "@/components/admin/catalog-manager/auto-organize"; import { AutoOrganize } from "@/components/admin/catalog-manager/auto-organize";
import { BcItemsEditor } from "@/components/admin/catalog-manager/bc-items-editor";
import { LiveCatalogPreview } from "@/components/admin/catalog-manager/live-catalog-preview"; import { LiveCatalogPreview } from "@/components/admin/catalog-manager/live-catalog-preview";
import { CatalogIcon, LAYOUT_COLORS } from "@/components/admin/catalog-tree"; import { CatalogIcon, LAYOUT_COLORS } from "@/components/admin/catalog-tree";
import Link from "@/components/link";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -133,10 +132,6 @@ function InlineEditorSession({
const [offersDirty, setOffersDirty] = useState(false); const [offersDirty, setOffersDirty] = useState(false);
const { dispatch, catalogType, canEdit } = useCatalogManager(); const { dispatch, catalogType, canEdit } = useCatalogManager();
const catQs = catalogTreeQs(catalogType); const catQs = catalogTreeQs(catalogType);
const pageHref =
catalogType === "bc"
? `/admin/catalog/builder-club/${pageId}`
: `/admin/catalog/${pageId}`;
const [page, setPage] = useState<PageDetail | null>(null); const [page, setPage] = useState<PageDetail | null>(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
@@ -425,16 +420,6 @@ function InlineEditorSession({
{t("preview.title")} {t("preview.title")}
</Button> </Button>
)} )}
<Link href={pageHref} target="_blank">
<Button
variant="ghost"
size="sm"
className="h-7 text-xs gap-1 text-[var(--admin-text)]"
>
<ExternalLink className="h-3 w-3" />
{t("fullEditor")}
</Button>
</Link>
</div> </div>
</div> </div>
@@ -671,7 +656,10 @@ function InlineEditorSession({
<ChevronRight className="h-3.5 w-3.5" /> <ChevronRight className="h-3.5 w-3.5" />
)} )}
Text fields Text fields
{(form.pageText1 || form.pageText2 || form.pageTextDetails) && ( {(form.pageText1 ||
form.pageText2 ||
form.pageTextDetails ||
form.pageTextTeaser) && (
<Badge variant="secondary" className="h-4 px-1 text-[9px]"> <Badge variant="secondary" className="h-4 px-1 text-[9px]">
has content has content
</Badge> </Badge>
@@ -713,6 +701,17 @@ function InlineEditorSession({
className="text-sm" className="text-sm"
/> />
</div> </div>
<div className="space-y-1.5">
<Label className="text-xs">Text Teaser</Label>
<Textarea
rows={2}
value={String(form.pageTextTeaser || "")}
onChange={(e) =>
update("pageTextTeaser", e.target.value || null)
}
className="text-sm"
/>
</div>
<div className="space-y-1.5"> <div className="space-y-1.5">
<Label className="text-xs">Special</Label> <Label className="text-xs">Special</Label>
<Input <Input
@@ -750,17 +749,7 @@ function InlineEditorSession({
className="flex-1 overflow-y-auto px-4 pb-4 mt-2" className="flex-1 overflow-y-auto px-4 pb-4 mt-2"
> >
{catalogType === "bc" ? ( {catalogType === "bc" ? (
<div className="rounded-md border border-dashed px-4 py-8 text-center text-sm text-muted-foreground space-y-3"> <BcItemsEditor pageId={page.id} canEdit={canEdit} />
<p>
BC items are managed on the full Builder Club page editor.
</p>
<Link href={pageHref} target="_blank">
<Button variant="outline" size="sm" className="gap-1">
<ExternalLink className="h-3 w-3" />
Open BC page editor
</Button>
</Link>
</div>
) : itemsError && !itemsData ? ( ) : itemsError && !itemsData ? (
<div className="flex items-center justify-center py-12"> <div className="flex items-center justify-center py-12">
<Button variant="outline" onClick={reloadItems}> <Button variant="outline" onClick={reloadItems}>
@@ -10,7 +10,7 @@ import {
Wand2, Wand2,
} from "lucide-react"; } from "lucide-react";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table/types"; import type { CatalogItemData } from "@/components/admin/catalog/catalog-items-table/types";
import { import {
getShopGridClass, getShopGridClass,
getShopTileSize, getShopTileSize,
@@ -97,8 +97,6 @@ export function SortableTree({
const { state, dispatch, catalogType, canEdit } = useCatalogManager(); const { state, dispatch, catalogType, canEdit } = useCatalogManager();
const t = useTranslations("pages.admin.catalog.workspace.duplicate"); const t = useTranslations("pages.admin.catalog.workspace.duplicate");
const catQs = catalogTreeQs(catalogType); const catQs = catalogTreeQs(catalogType);
const pageBase =
catalogType === "bc" ? "/admin/catalog/builder-club" : "/admin/catalog";
const { confirm, dialog: confirmDialog } = useConfirmDialog(); const { confirm, dialog: confirmDialog } = useConfirmDialog();
const { const {
nodes, nodes,
@@ -713,8 +711,6 @@ export function SortableTree({
}, },
[state.editorDirty, t, canEdit, nodes, catalogType], [state.editorDirty, t, canEdit, nodes, catalogType],
); );
const handleOpenFull = (id: number) =>
window.open(`${pageBase}/${id}`, "_blank");
// ── Expand/Collapse all ───────────────────────────────────── // ── Expand/Collapse all ─────────────────────────────────────
const handleExpandAll = useCallback(() => { const handleExpandAll = useCallback(() => {
@@ -813,10 +809,8 @@ export function SortableTree({
}; };
const activeNode = activeId ? flatItems.find((n) => n.id === activeId) : null; const activeNode = activeId ? flatItems.find((n) => n.id === activeId) : null;
const noop = () => {}; const noop = () => {};
// Shared props builder for tree items
// biome-ignore lint/correctness/useExhaustiveDependencies: explicitly chosen here, see surrounding code
const getItemProps = useCallback( const getItemProps = useCallback(
(node: FlatTreeNode): Omit<TreeItemProps, "sortMode" | "isOverlay"> => ({ (node: FlatTreeNode): Omit<TreeItemProps, "sortMode" | "isOverlay"> => ({
node, node,
@@ -831,7 +825,6 @@ export function SortableTree({
onToggleVisible: handleToggleVisible, onToggleVisible: handleToggleVisible,
onDuplicate: handleDuplicate, onDuplicate: handleDuplicate,
onDelete: handleDelete, onDelete: handleDelete,
onOpenFull: handleOpenFull,
isLoading: loadingIds.has(node.id), isLoading: loadingIds.has(node.id),
}), }),
[ [
@@ -1129,7 +1122,6 @@ export function SortableTree({
onToggleVisible={noop} onToggleVisible={noop}
onDuplicate={noop} onDuplicate={noop}
onDelete={noop} onDelete={noop}
onOpenFull={noop}
/> />
)} )}
</DragOverlay> </DragOverlay>
@@ -5,7 +5,6 @@ import { CSS } from "@dnd-kit/utilities";
import { import {
ChevronRight, ChevronRight,
Copy as CopyIcon, Copy as CopyIcon,
ExternalLink,
Eye, Eye,
EyeOff, EyeOff,
EyeOff as EyeOffIcon, EyeOff as EyeOffIcon,
@@ -61,7 +60,6 @@ export interface TreeItemProps {
onToggleVisible: (id: number, currentVisible: string) => void; onToggleVisible: (id: number, currentVisible: string) => void;
onDuplicate: (id: number) => void; onDuplicate: (id: number) => void;
onDelete: (id: number) => void; onDelete: (id: number) => void;
onOpenFull: (id: number) => void;
isLoading?: boolean; isLoading?: boolean;
} }
@@ -114,7 +112,6 @@ export const TreeItemContent = React.memo(
onToggleVisible, onToggleVisible,
onDuplicate, onDuplicate,
onDelete, onDelete,
onOpenFull,
isLoading, isLoading,
}: TreeItemProps & { }: TreeItemProps & {
isDragging?: boolean; isDragging?: boolean;
@@ -262,10 +259,6 @@ export const TreeItemContent = React.memo(
<Pencil className="mr-2 h-3.5 w-3.5" /> <Pencil className="mr-2 h-3.5 w-3.5" />
Edit Edit
</ContextMenuItem> </ContextMenuItem>
<ContextMenuItem onClick={() => onOpenFull(node.id)}>
<ExternalLink className="mr-2 h-3.5 w-3.5" />
Open full editor
</ContextMenuItem>
<ContextMenuItem <ContextMenuItem
onClick={() => navigator.clipboard.writeText(String(node.id))} onClick={() => navigator.clipboard.writeText(String(node.id))}
> >
@@ -1,7 +1,7 @@
"use client"; "use client";
import { Package } from "lucide-react"; import { Package } from "lucide-react";
import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table/types"; import type { CatalogItemData } from "@/components/admin/catalog/catalog-items-table/types";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { import {
getShopGridClass, getShopGridClass,
+2 -2
View File
@@ -1,8 +1,8 @@
"use client"; "use client";
import { Crown, Package, Sparkles } from "lucide-react"; import { Crown, Package, Sparkles } from "lucide-react";
import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table/types"; import type { CatalogItemData } from "@/components/admin/catalog/catalog-items-table/types";
import { getPointsLabel } from "@/app/admin/catalog/[id]/catalog-items-table/types"; import { getPointsLabel } from "@/components/admin/catalog/catalog-items-table/types";
import { import {
CurrencyIcon, CurrencyIcon,
currencyKindFromPointsType, currencyKindFromPointsType,
@@ -9,8 +9,8 @@ import {
previewBulkOffers, previewBulkOffers,
undoBulkOffers, undoBulkOffers,
} from "@/actions/catalog-bulk"; } from "@/actions/catalog-bulk";
import { MovePageCombobox } from "@/app/admin/catalog/[id]/catalog-items-table/field-helpers"; import { MovePageCombobox } from "@/components/admin/catalog/catalog-items-table/field-helpers";
import { POINTS_TYPES } from "@/app/admin/catalog/[id]/catalog-items-table/types"; import { POINTS_TYPES } from "@/components/admin/catalog/catalog-items-table/types";
import { import {
CurrencyIcon, CurrencyIcon,
currencyKindFromPointsType, currencyKindFromPointsType,
@@ -7,7 +7,7 @@ import {
catalogDuplicateDestinations, catalogDuplicateDestinations,
previewCatalogDuplicate, previewCatalogDuplicate,
} from "@/actions/catalog-duplicate"; } from "@/actions/catalog-duplicate";
import { MovePageCombobox } from "@/app/admin/catalog/[id]/catalog-items-table/field-helpers"; import { MovePageCombobox } from "@/components/admin/catalog/catalog-items-table/field-helpers";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Dialog, Dialog,
@@ -1,4 +1,4 @@
import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table/types"; import type { CatalogItemData } from "@/components/admin/catalog/catalog-items-table/types";
export const OFFER_FILTERS = [ export const OFFER_FILTERS = [
"all", "all",
+1 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table/types"; import type { CatalogItemData } from "@/components/admin/catalog/catalog-items-table/types";
import { import {
buildSortOrders, buildSortOrders,
groupSuggestedCategories, groupSuggestedCategories,
+1 -1
View File
@@ -1,4 +1,4 @@
import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table/types"; import type { CatalogItemData } from "@/components/admin/catalog/catalog-items-table/types";
import type { CatalogLayout } from "@/lib/catalog-layouts"; import type { CatalogLayout } from "@/lib/catalog-layouts";
export type SortStrategy = export type SortStrategy =
+2 -2
View File
@@ -452,11 +452,11 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
matchPrefixes: ["/admin/catalog"], matchPrefixes: ["/admin/catalog"],
}, },
{ {
href: "/admin/catalog/maintenance", href: "/admin/studio/maintenance",
labelKey: "catalogMaintenance", labelKey: "catalogMaintenance",
icon: Wrench, icon: Wrench,
permission: PERMS.CATALOG_EDIT, permission: PERMS.CATALOG_EDIT,
matchPrefixes: ["/admin/catalog/maintenance"], matchPrefixes: ["/admin/studio/maintenance"],
}, },
{ {
href: "/admin/items", href: "/admin/items",