"use client"; import { useState, useTransition } from "react"; import { useRouter } from "next/navigation"; import { DownloadIcon, Trash2Icon, XIcon } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { TagPicker } from "@/components/tags/tag-picker"; import type { TagValue } from "@/components/tags/tag-chip"; import type { TagKind } from "@prisma/client"; /** * What you can do to a selection. * * Appears only when something is ticked — a toolbar you did not ask for is * clutter — and it lives inside the list component rather than beside it, so * filtering down to nothing does not unmount the bar and silently throw the * selection away. * * Tagging adds and removes rather than replacing. "Tag these nine as fintech" * must not mean stripping the size and location off all nine, which is what a * replace would do. */ export function SelectionBar({ ids, what, tagKind, onTag, onArchive, exportHref, onClear, extra, }: { ids: string[]; what: { one: string; many: string }; tagKind: TagKind; onTag: (ids: string[], change: { add?: string[]; remove?: string[] }) => Promise; onArchive: (ids: string[]) => Promise<{ archived: { title: string }[] }>; exportHref: string; onClear: () => void; /** Anything only one of the two lists offers. */ extra?: React.ReactNode; }) { const router = useRouter(); const [pending, startTransition] = useTransition(); const [adding, setAdding] = useState([]); const noun = ids.length === 1 ? what.one : what.many; const act = (work: () => Promise, done: (result: never) => string) => startTransition(async () => { try { const result = await work(); toast.success(done(result as never)); onClear(); router.refresh(); } catch (error) { toast.error(error instanceof Error ? error.message : "Could not do that."); } }); const tag = (next: TagValue[]) => { const added = next.filter((tag) => !adding.some((had) => had.id === tag.id)); const removed = adding.filter((had) => !next.some((tag) => tag.id === had.id)); setAdding(next); if (added.length === 0 && removed.length === 0) return; startTransition(async () => { try { await onTag(ids, { add: added.map((t) => t.id), remove: removed.map((t) => t.id) }); router.refresh(); } catch (error) { toast.error(error instanceof Error ? error.message : "Could not change those tags."); } }); }; return (
{ids.length} selected
{extra}
); }