1"use client";
2
3
11import { Dialog, DialogFooter } from "@/components/dialogs/dialog";
12import { useBulkPages } from "@/hooks/use-pages";
13import { toast, useUiStore, type BulkAction } from "@/stores/ui-store";
14
15const ACTION_COPY: Record<BulkAction, { verb: string; confirm: string; noun: string }> = {
16 trash: { verb: "Move to trash", confirm: "Move to trash", noun: "page" },
17 archive: { verb: "Archive", confirm: "Archive", noun: "page" },
18 unarchive: { verb: "Unarchive", confirm: "Unarchive", noun: "page" },
19 favorite: { verb: "Add to favorites", confirm: "Add to favorites", noun: "page" },
20 unfavorite: { verb: "Remove from favorites", confirm: "Remove from favorites", noun: "page" }
21};
22
23export function BulkActionsDialog() {
24 const bulkOpen = useUiStore((s) => s.bulkOpen);
25 const bulkAction = useUiStore((s) => s.bulkAction);
26 const pageSelection = useUiStore((s) => s.pageSelection);
27 const setBulk = useUiStore((s) => s.setBulk);
28 const bulk = useBulkPages(pageSelection.workspaceId);
29
30 if (!bulkOpen || !bulkAction) return null;
31 const copy = ACTION_COPY[bulkAction];
32 const count = pageSelection.ids.length;
33 const destructive = bulkAction === "trash";
34
35 const onConfirm = () => {
36 const ids = pageSelection.ids;
37 bulk.mutate(
38 { action: bulkAction, ids },
39 {
40 onSuccess: (res) => {
41 const { succeeded, failed } = res;
42 const unit = (n: number) => `${n} ${n === 1 ? copy.noun : copy.noun + "s"}`;
43 if (failed.length === 0) {
44 toast("success", `${copy.confirm} ${unit(succeeded.length)}`);
45 } else if (succeeded.length === 0) {
46 toast("error", `${copy.confirm} failed`, `${unit(failed.length)} — no permission or missing.`);
47 } else {
48 toast("info", `${copy.confirm}: ${unit(succeeded.length)}`, `${unit(failed.length)} skipped (no permission or missing).`);
49 }
50 },
51 onError: (e: Error) => toast("error", `${copy.confirm} failed`, e.message)
52 }
53 );
54 };
55
56 return (
57 <Dialog
58 open={bulkOpen}
59 title={`${copy.verb} ${count} ${count === 1 ? copy.noun : copy.noun + "s"}`}
60 description={
61 destructive
62 ? "This moves the selected pages to the workspace trash. You can restore them from Trash."
63 : "This applies to every selected page in this workspace."
64 }
65 onClose={() => setBulk(false)}
66 footer={
67 <DialogFooter
68 onCancel={() => setBulk(false)}
69 onAction={onConfirm}
70 actionLabel={copy.confirm}
71 busy={bulk.isPending}
72 destructive={destructive}
73 />
74 }
75 >
76 <p className="text-sm text-muted-foreground" data-testid="bulk-dialog-body">
77 {count} {count === 1 ? copy.noun : copy.noun + "s"} will be affected. Pages you cannot
78 edit are skipped and reported.
79 </p>
80 </Dialog>
81 );
82}
83