feat(catalog): add reviewed bulk edits and complete category duplication
This commit is contained in:
1 parent
0bedc04692
commit
55a47949f1
26 files changed
+2504
-85
No files matched your search
@@ -34,6 +34,7 @@ import {
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
@@ -80,6 +81,7 @@ import {
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { BulkOfferEditor } from "@/features/catalog/components/bulk-offer-editor";
|
||||
import { useServerAction } from "@/hooks/use-server-action";
|
||||
import { getSuggestions, recordMove } from "@/lib/move-suggestions";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -167,6 +169,10 @@ export function CatalogItemsTable({
|
||||
allPages,
|
||||
}: CatalogItemsTableProps) {
|
||||
const router = useRouter();
|
||||
const bulk = useTranslations("pages.admin.catalog.bulk");
|
||||
const [bulkRefreshFrom, setBulkRefreshFrom] = useState<
|
||||
CatalogItemData[] | null
|
||||
>(null);
|
||||
const { run, isPending } = useServerAction();
|
||||
const { confirm, dialog: confirmDialog } = useConfirmDialog();
|
||||
const [addOpen, setAddOpen] = useState(false);
|
||||
@@ -294,7 +300,26 @@ export function CatalogItemsTable({
|
||||
return itemOrder.some((id, idx) => id !== originalOrder[idx]);
|
||||
}, [items, itemOrder]);
|
||||
|
||||
const hasUnsavedChanges = modifiedItems.length > 0 || hasOrderChanges;
|
||||
const hasUnsavedChanges =
|
||||
bulkRefreshFrom === null && (modifiedItems.length > 0 || hasOrderChanges);
|
||||
useEffect(() => {
|
||||
if (bulkRefreshFrom === null || items === bulkRefreshFrom) return;
|
||||
setInlineEdits(
|
||||
Object.fromEntries(
|
||||
items.map((item) => [
|
||||
item.id,
|
||||
{
|
||||
costCredits: item.costCredits,
|
||||
costPoints: item.costPoints,
|
||||
pointsType: item.pointsType,
|
||||
amount: item.amount,
|
||||
},
|
||||
]),
|
||||
),
|
||||
);
|
||||
setItemOrder(items.map((item) => item.id));
|
||||
setBulkRefreshFrom(null);
|
||||
}, [items, bulkRefreshFrom]);
|
||||
useEffect(() => {
|
||||
onDirtyChange?.(hasUnsavedChanges);
|
||||
}, [hasUnsavedChanges, onDirtyChange]);
|
||||
@@ -904,7 +929,19 @@ export function CatalogItemsTable({
|
||||
return (
|
||||
<>
|
||||
{confirmDialog}
|
||||
<Card>
|
||||
{bulkRefreshFrom !== null && (
|
||||
<p
|
||||
role="status"
|
||||
className="flex items-center gap-2 text-sm text-muted-foreground"
|
||||
>
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
{bulk("refreshing")}
|
||||
</p>
|
||||
)}
|
||||
<Card
|
||||
inert={bulkRefreshFrom !== null}
|
||||
aria-busy={bulkRefreshFrom !== null}
|
||||
>
|
||||
<CardHeader className="space-y-3">
|
||||
<div className="flex flex-row items-center justify-between gap-4">
|
||||
<div className="min-w-0">
|
||||
@@ -958,6 +995,21 @@ export function CatalogItemsTable({
|
||||
<>
|
||||
{selected.size > 0 && (
|
||||
<>
|
||||
<BulkOfferEditor
|
||||
ids={[...selected]}
|
||||
allPages={allPages}
|
||||
disabled={
|
||||
hasUnsavedChanges ||
|
||||
isPending ||
|
||||
savingItemId !== null
|
||||
}
|
||||
onApplied={() => {
|
||||
setBulkRefreshFrom(items);
|
||||
setSelected(new Set());
|
||||
if (onRefresh) onRefresh();
|
||||
else router.refresh();
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
|
||||
Reference in new issue
Block a user