import { useEffect } from "react"; import { useQueryStates } from "nuqs"; import { FileText, ListFilter, Moon, Search, Sun, X } from "lucide-react"; import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, } from "@/components/ui/command"; import { useTheme } from "@/hooks/ThemeContext"; import { useHeaderConfig } from "@/hooks/useHeaderConfig"; import { tableParams, tableParamsOptions } from "@/lib/tableParams"; import { WORKFLOW_STATES } from "@/lib/workflow"; import { cn } from "@/lib/utils"; interface Props { open: boolean; onOpenChange: (open: boolean) => void; onNavigate: (screenUuid: string) => void; } /** * ⌘K palette — jump between screens and apply status filters without leaving * the keyboard. This is a queue-triage app; reaching for the mouse to change a * filter is the slow path. */ export default function CommandPalette({ open, onOpenChange, onNavigate }: Props) { const { theme, toggleTheme } = useTheme(); const { navItems } = useHeaderConfig(); const [params, setParams] = useQueryStates(tableParams, tableParamsOptions); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === "k" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); onOpenChange(!open); } }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [open, onOpenChange]); const run = (fn: () => void) => { fn(); onOpenChange(false); }; const filtersActive = params.status.length > 0 || !!params.q; return ( No matches. {navItems.length > 0 && ( {navItems.map((item) => ( run(() => onNavigate(item.screen_uuid))} > {item.label} ))} )} {WORKFLOW_STATES.map((s) => { const selected = params.status.includes(s.name); return ( run(() => setParams({ status: selected ? params.status.filter((x) => x !== s.name) : [...params.status, s.name], page: 1, }), ) } > {s.label} {selected && active} ); })} {filtersActive && ( run(() => setParams({ status: [], q: "", page: 1 }))} > Clear all filters )} run(() => { // Defer so the dialog has released focus first. requestAnimationFrame(() => { document .querySelector('input[aria-label="Search records"]') ?.focus(); }); }) } > Search records / run(toggleTheme)}> {theme === "dark" ? ( ) : ( )} Switch to {theme === "dark" ? "light" : "dark"} mode Tip: tiles on the dashboard also filter the queue ); }