"use client"; import { useState, useTransition } from "react"; import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import { BookmarkIcon, CheckIcon, PlusIcon, Trash2Icon } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { deleteSavedViewAction, saveViewAction } from "@/server/actions"; import { cn } from "@/lib/utils"; export type SavedViewRow = { id: string; name: string; query: string }; /** * Named cuts of the pipeline. * * The URL is already the state of this screen, so a saved view is a name for a * query string and the whole feature is a list of links. It lives in a menu * rather than as another row of chips because the filter strip is already the * busiest thing on the page, and a view is something you reach for * occasionally rather than something you scan. */ export function SavedViews({ views, current }: { views: SavedViewRow[]; current: string }) { const router = useRouter(); const params = useSearchParams(); const [open, setOpen] = useState(false); const [naming, setNaming] = useState(false); const [name, setName] = useState(""); const [pending, startTransition] = useTransition(); const query = params.toString(); const active = views.find((view) => view.query === current); const save = () => { const trimmed = name.trim(); if (!trimmed) return; startTransition(async () => { try { await saveViewAction(trimmed, query); setName(""); setNaming(false); setOpen(false); toast.success(`Saved "${trimmed}"`); router.refresh(); } catch (error) { toast.error(error instanceof Error ? error.message : "Could not save that view."); } }); }; const remove = (view: SavedViewRow) => startTransition(async () => { try { await deleteSavedViewAction(view.id); toast.success(`Deleted "${view.name}"`); router.refresh(); } catch (error) { toast.error(error instanceof Error ? error.message : "Could not delete that view."); } }); return ( {views.length === 0 && !naming && (

Filter and sort the pipeline the way you want it, then save it here and it becomes one click.

)} {views.map((view) => ( {view.name} {/* A destructive control inside a menu item: it stops the click from also navigating, which is the one thing that would make deleting a view feel like a trap. */} ))} {views.length > 0 && } {naming ? (
setName(event.target.value)} onKeyDown={(event) => { if (event.key === "Enter") save(); if (event.key === "Escape") setNaming(false); }} placeholder="Chasing" className="h-8 md:h-8" />
) : ( { event.preventDefault(); setNaming(true); }} > Save this view )}
); }