"use client"; import { Check, ChevronsUpDown, Star } from "lucide-react"; import { useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command"; import { Label } from "@/components/ui/label"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Switch } from "@/components/ui/switch"; import { cn } from "@/lib/utils"; // ── Toggle field ────────────────────────────────────────────── export function ToggleField({ label, checked, value, onChange, disabled, }: { label: string; /** Boolean checked state */ checked?: boolean; /** Number or string (0/1 or '0'/'1') — converted to boolean automatically */ value?: number | string; onChange: (v: boolean) => void; disabled?: boolean; }) { const isChecked = checked ?? (value === 1 || value === "1"); return (
); } // ── Interaction Type Combobox ────────────────────────────────── export function InteractionTypeCombobox({ value, onChange, types, disabled, }: { value: string; onChange: (v: string) => void; types: string[]; disabled?: boolean; }) { const [open, setOpen] = useState(false); const [inputValue, setInputValue] = useState(""); const filtered = useMemo(() => { if (!inputValue.trim()) return types.slice(0, 50); const q = inputValue.toLowerCase(); return types.filter((t) => t.toLowerCase().includes(q)).slice(0, 50); }, [types, inputValue]); return ( {inputValue.trim() ? ( ) : ( "No types found." )} {filtered.map((t) => ( { onChange(t); setOpen(false); setInputValue(""); }} > {t} ))} ); } // ── Move Page Combobox with smart suggestions ───────────────── export function MovePageCombobox({ value, onChange, pages, currentPageId, suggestedPageIds, }: { value: number | null; onChange: (id: number | null) => void; pages: { id: number; caption: string }[]; currentPageId: number; /** Page IDs to show first as suggestions (from move-suggestions.ts) */ suggestedPageIds?: number[]; }) { const [open, setOpen] = useState(false); const options = useMemo( () => pages.filter((p) => p.id !== currentPageId), [pages, currentPageId], ); const selectedPage = value ? pages.find((p) => p.id === value) : null; // Build suggested pages list const suggested = useMemo(() => { if (!suggestedPageIds?.length) return []; const pageMap = new Map(options.map((p) => [p.id, p])); return suggestedPageIds.map((id) => pageMap.get(id)).filter(Boolean) as { id: number; caption: string; }[]; }, [suggestedPageIds, options]); // Non-suggested pages (exclude those already in suggestions) const suggestedSet = useMemo( () => new Set(suggested.map((s) => s.id)), [suggested], ); const remaining = useMemo( () => options.filter((p) => !suggestedSet.has(p.id)), [options, suggestedSet], ); return ( No page found. {/* Suggested pages */} {suggested.length > 0 && ( {suggested.map((page) => ( { onChange(page.id); setOpen(false); }} > #{page.id} {page.caption} ))} )} {/* All pages */} 0 ? "All pages" : undefined} > {remaining.map((page) => ( { onChange(page.id); setOpen(false); }} > #{page.id} {page.caption} ))} ); }