Replace standalone catalog editor with the unified Visual Catalog studio
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:
1 parent
1a9e1e791a
commit
1bbd809b43
36 files changed
+526
-2750
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
@@ -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 { CatalogItemsBc, CatalogPagesBc, db } from "@/lib/db";
|
||||
import { CatalogPagesBc, db } from "@/lib/db";
|
||||
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({
|
||||
params: paramsPromise,
|
||||
@@ -18,50 +33,13 @@ export default async function BcPageDetailPage({
|
||||
redirect("/admin");
|
||||
}
|
||||
|
||||
const canEdit = canAccess(permissions, PERMS.CATALOG_EDIT, session.user.rank);
|
||||
|
||||
const [bcPage] = await db
|
||||
.select()
|
||||
.select({ id: CatalogPagesBc.id, parentId: CatalogPagesBc.parentId })
|
||||
.from(CatalogPagesBc)
|
||||
.where(eq(CatalogPagesBc.id, id))
|
||||
.limit(1);
|
||||
if (!bcPage) notFound();
|
||||
|
||||
const bcItems = await db
|
||||
.select()
|
||||
.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}
|
||||
/>
|
||||
);
|
||||
const rootId = (await topAncestorId(bcPage.parentId)) ?? id;
|
||||
redirect(`/admin/catalog?catalog=bc&root=${rootId}&category=${id}`);
|
||||
}
|
||||
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,7 +1,4 @@
|
||||
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";
|
||||
|
||||
export default async function CatalogMaintenancePage() {
|
||||
@@ -9,27 +6,5 @@ export default async function CatalogMaintenancePage() {
|
||||
if (!canAccess(permissions, PERMS.CATALOG_VIEW, session.user.rank)) {
|
||||
redirect("/admin");
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
redirect("/admin/studio/maintenance");
|
||||
}
|
||||
+98
-503
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,6 @@
|
||||
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";
|
||||
|
||||
@@ -8,9 +10,26 @@ export default async function StudioCatalogMaintenancePage() {
|
||||
redirect("/admin");
|
||||
}
|
||||
|
||||
const canRepair = canAccess(
|
||||
permissions,
|
||||
PERMS.CATALOG_EDIT,
|
||||
session.user.rank,
|
||||
);
|
||||
const t = await getTranslations("pages.admin.catalog.integrity");
|
||||
return (
|
||||
<div className="min-h-0">
|
||||
<CatalogMaintenancePanel />
|
||||
<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 />
|
||||
</details>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { asc, eq } from "drizzle-orm";
|
||||
import { apiError, apiOk } from "@/lib/api";
|
||||
import { withAdmin } from "@/lib/api-handler";
|
||||
import { CatalogItemsBc, db } from "@/lib/db";
|
||||
import { PERMS } from "@/lib/permissions";
|
||||
import { loadCatalogItemsData } from "@/lib/services/catalog-items-loader";
|
||||
|
||||
@@ -16,6 +18,15 @@ export const GET = withAdmin(
|
||||
if (!Number.isFinite(pageId) || pageId <= 0) {
|
||||
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);
|
||||
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 { toast } from "sonner";
|
||||
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 { useCatalogManager } from "@/components/admin/catalog-manager/use-catalog-store";
|
||||
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 { toast } from "sonner";
|
||||
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 {
|
||||
Select,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -47,7 +47,7 @@ export function CatalogManagerWorkspace({
|
||||
return (
|
||||
<section
|
||||
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
|
||||
key={catalogType}
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
AlertCircle,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
ExternalLink,
|
||||
Languages,
|
||||
Loader2,
|
||||
MonitorPlay,
|
||||
@@ -20,16 +19,16 @@ import { updateBcPage } from "@/actions/catalog-bc";
|
||||
import {
|
||||
type CatalogItemData,
|
||||
CatalogItemsTable,
|
||||
} from "@/app/admin/catalog/[id]/catalog-items-table";
|
||||
import { CatalogTranslateTab } from "@/app/admin/catalog/[id]/catalog-translate-tab";
|
||||
} from "@/components/admin/catalog/catalog-items-table";
|
||||
import { CatalogTranslateTab } from "@/components/admin/catalog/catalog-translate-tab";
|
||||
import { BulkImportItems } from "@/components/admin/catalog/bulk-import-items";
|
||||
import { CatalogImagePicker } from "@/components/admin/catalog/catalog-image-picker";
|
||||
import { IconPicker } from "@/components/admin/catalog/icon-picker";
|
||||
import { QuickAddFurni } from "@/components/admin/catalog/quick-add-furni";
|
||||
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 { CatalogIcon, LAYOUT_COLORS } from "@/components/admin/catalog-tree";
|
||||
import Link from "@/components/link";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -133,10 +132,6 @@ function InlineEditorSession({
|
||||
const [offersDirty, setOffersDirty] = useState(false);
|
||||
const { dispatch, catalogType, canEdit } = useCatalogManager();
|
||||
const catQs = catalogTreeQs(catalogType);
|
||||
const pageHref =
|
||||
catalogType === "bc"
|
||||
? `/admin/catalog/builder-club/${pageId}`
|
||||
: `/admin/catalog/${pageId}`;
|
||||
const [page, setPage] = useState<PageDetail | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
@@ -425,17 +420,7 @@ function InlineEditorSession({
|
||||
{t("preview.title")}
|
||||
</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>
|
||||
|
||||
{/* Dirty banner */}
|
||||
@@ -671,7 +656,10 @@ function InlineEditorSession({
|
||||
<ChevronRight className="h-3.5 w-3.5" />
|
||||
)}
|
||||
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]">
|
||||
has content
|
||||
</Badge>
|
||||
@@ -713,6 +701,17 @@ function InlineEditorSession({
|
||||
className="text-sm"
|
||||
/>
|
||||
</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">
|
||||
<Label className="text-xs">Special</Label>
|
||||
<Input
|
||||
@@ -750,17 +749,7 @@ function InlineEditorSession({
|
||||
className="flex-1 overflow-y-auto px-4 pb-4 mt-2"
|
||||
>
|
||||
{catalogType === "bc" ? (
|
||||
<div className="rounded-md border border-dashed px-4 py-8 text-center text-sm text-muted-foreground space-y-3">
|
||||
<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>
|
||||
<BcItemsEditor pageId={page.id} canEdit={canEdit} />
|
||||
) : itemsError && !itemsData ? (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<Button variant="outline" onClick={reloadItems}>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
Wand2,
|
||||
} from "lucide-react";
|
||||
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 {
|
||||
getShopGridClass,
|
||||
getShopTileSize,
|
||||
|
||||
@@ -97,8 +97,6 @@ export function SortableTree({
|
||||
const { state, dispatch, catalogType, canEdit } = useCatalogManager();
|
||||
const t = useTranslations("pages.admin.catalog.workspace.duplicate");
|
||||
const catQs = catalogTreeQs(catalogType);
|
||||
const pageBase =
|
||||
catalogType === "bc" ? "/admin/catalog/builder-club" : "/admin/catalog";
|
||||
const { confirm, dialog: confirmDialog } = useConfirmDialog();
|
||||
const {
|
||||
nodes,
|
||||
@@ -713,8 +711,6 @@ export function SortableTree({
|
||||
},
|
||||
[state.editorDirty, t, canEdit, nodes, catalogType],
|
||||
);
|
||||
const handleOpenFull = (id: number) =>
|
||||
window.open(`${pageBase}/${id}`, "_blank");
|
||||
|
||||
// ── Expand/Collapse all ─────────────────────────────────────
|
||||
const handleExpandAll = useCallback(() => {
|
||||
@@ -813,10 +809,8 @@ export function SortableTree({
|
||||
};
|
||||
|
||||
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(
|
||||
(node: FlatTreeNode): Omit<TreeItemProps, "sortMode" | "isOverlay"> => ({
|
||||
node,
|
||||
@@ -831,7 +825,6 @@ export function SortableTree({
|
||||
onToggleVisible: handleToggleVisible,
|
||||
onDuplicate: handleDuplicate,
|
||||
onDelete: handleDelete,
|
||||
onOpenFull: handleOpenFull,
|
||||
isLoading: loadingIds.has(node.id),
|
||||
}),
|
||||
[
|
||||
@@ -1129,7 +1122,6 @@ export function SortableTree({
|
||||
onToggleVisible={noop}
|
||||
onDuplicate={noop}
|
||||
onDelete={noop}
|
||||
onOpenFull={noop}
|
||||
/>
|
||||
)}
|
||||
</DragOverlay>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { CSS } from "@dnd-kit/utilities";
|
||||
import {
|
||||
ChevronRight,
|
||||
Copy as CopyIcon,
|
||||
ExternalLink,
|
||||
Eye,
|
||||
EyeOff,
|
||||
EyeOff as EyeOffIcon,
|
||||
@@ -61,7 +60,6 @@ export interface TreeItemProps {
|
||||
onToggleVisible: (id: number, currentVisible: string) => void;
|
||||
onDuplicate: (id: number) => void;
|
||||
onDelete: (id: number) => void;
|
||||
onOpenFull: (id: number) => void;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
@@ -114,7 +112,6 @@ export const TreeItemContent = React.memo(
|
||||
onToggleVisible,
|
||||
onDuplicate,
|
||||
onDelete,
|
||||
onOpenFull,
|
||||
isLoading,
|
||||
}: TreeItemProps & {
|
||||
isDragging?: boolean;
|
||||
@@ -262,10 +259,6 @@ export const TreeItemContent = React.memo(
|
||||
<Pencil className="mr-2 h-3.5 w-3.5" />
|
||||
Edit
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem onClick={() => onOpenFull(node.id)}>
|
||||
<ExternalLink className="mr-2 h-3.5 w-3.5" />
|
||||
Open full editor
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(String(node.id))}
|
||||
>
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
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 {
|
||||
getShopGridClass,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { Crown, Package, Sparkles } from "lucide-react";
|
||||
import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table/types";
|
||||
import { getPointsLabel } from "@/app/admin/catalog/[id]/catalog-items-table/types";
|
||||
import type { CatalogItemData } from "@/components/admin/catalog/catalog-items-table/types";
|
||||
import { getPointsLabel } from "@/components/admin/catalog/catalog-items-table/types";
|
||||
import {
|
||||
CurrencyIcon,
|
||||
currencyKindFromPointsType,
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
previewBulkOffers,
|
||||
undoBulkOffers,
|
||||
} from "@/actions/catalog-bulk";
|
||||
import { MovePageCombobox } from "@/app/admin/catalog/[id]/catalog-items-table/field-helpers";
|
||||
import { POINTS_TYPES } from "@/app/admin/catalog/[id]/catalog-items-table/types";
|
||||
import { MovePageCombobox } from "@/components/admin/catalog/catalog-items-table/field-helpers";
|
||||
import { POINTS_TYPES } from "@/components/admin/catalog/catalog-items-table/types";
|
||||
import {
|
||||
CurrencyIcon,
|
||||
currencyKindFromPointsType,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
catalogDuplicateDestinations,
|
||||
previewCatalogDuplicate,
|
||||
} 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 {
|
||||
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 = [
|
||||
"all",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 {
|
||||
buildSortOrders,
|
||||
groupSuggestedCategories,
|
||||
|
||||
@@ -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";
|
||||
|
||||
export type SortStrategy =
|
||||
|
||||
@@ -452,11 +452,11 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
matchPrefixes: ["/admin/catalog"],
|
||||
},
|
||||
{
|
||||
href: "/admin/catalog/maintenance",
|
||||
href: "/admin/studio/maintenance",
|
||||
labelKey: "catalogMaintenance",
|
||||
icon: Wrench,
|
||||
permission: PERMS.CATALOG_EDIT,
|
||||
matchPrefixes: ["/admin/catalog/maintenance"],
|
||||
matchPrefixes: ["/admin/studio/maintenance"],
|
||||
},
|
||||
{
|
||||
href: "/admin/items",
|
||||
|
||||
Reference in new issue
Block a user