"use client"; import { useEffect, useState, useTransition } from "react"; import { useRouter } from "next/navigation"; import { Columns3Icon } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Checkbox } from "@/components/ui/checkbox"; import { setPipelineFieldsAction } from "@/server/actions"; import { FIELDS, VIEW_LABEL, storedFields, type PipelineView, } from "@/lib/pipeline-fields"; /** * How much each view shows before you open anything. * * It sits in the pipeline toolbar rather than in Settings, because it is about * the screen you are looking at and the answer is different for each of the * three. The catalogue is per-view for the same reason: a board card and a * calendar chip have nothing in common except that both can be too busy. * * The choice is stored on the profile, not in the URL: a saved view is a cut of * the data, and how much of each row you like seeing is not. */ export function FieldsMenu({ view, visible, }: { view: PipelineView; visible: string[]; }) { const router = useRouter(); const [pending, startTransition] = useTransition(); // Local, not derived from the prop. The prop only moves after the action and // router.refresh() have both landed, so two clicks in quick succession would // each compute their payload from the pre-first-click set and the second // would silently undo the first. It also means the box responds on click // rather than after a round trip. const [on, setOn] = useState>(() => new Set(visible)); useEffect(() => setOn(new Set(visible)), [visible]); const catalogue = FIELDS[view]; const toggle = (key: string) => { const next = new Set(on); if (next.has(key)) next.delete(key); else next.add(key); setOn(next); startTransition(async () => { try { await setPipelineFieldsAction(view, storedFields(view, next)); router.refresh(); } catch (error) { setOn(on); toast.error(error instanceof Error ? error.message : "Could not save that."); } }); }; const reset = () => startTransition(async () => { try { // The empty array is "I have never chosen", which is what puts the // defaults back — including any field added to the catalogue since. await setPipelineFieldsAction(view, []); router.refresh(); } catch (error) { toast.error(error instanceof Error ? error.message : "Could not reset that."); } }); return (

What the {VIEW_LABEL[view].toLowerCase()} shows before you open anything.

    {catalogue.map((field) => (
  • ))}
); }