"use client"; import { useState } from "react"; import { BriefcaseIcon, Building2Icon, CircleUserRoundIcon, FileTextIcon, StickyNoteIcon, UserRoundIcon, XIcon, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Command, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from "@/components/ui/command"; import { SUBJECT_LABEL, type TaskSubjectKind } from "@/lib/task-subject"; import { cn } from "@/lib/utils"; /** * What a task is about, picked from everything it could be about. * * One list across six kinds rather than a kind selector and then a record * selector. Nobody thinks "a contact task about Priya"; they think "about * Priya" — so you type Priya and the row says which kind she is. The kinds are * headings, and the search runs across all of them at once. * * Sending the choice up as `{kind, id}` rather than five nullable props keeps * the "at most one" rule in one place instead of in every caller. */ export type SubjectOption = { kind: TaskSubjectKind; id: string; label: string; /** Second line, where the label alone would be ambiguous. */ hint?: string; }; const ICON: Record = { application: BriefcaseIcon, company: Building2Icon, contact: CircleUserRoundIcon, resume: FileTextIcon, role: UserRoundIcon, note: StickyNoteIcon, }; /** The order the groups appear in. Most-attached first. */ const ORDER: TaskSubjectKind[] = ["application", "contact", "company", "resume", "role", "note"]; export function SubjectPicker({ value, options, onChange, className, size = "default", }: { value: { kind: TaskSubjectKind; id: string } | null; options: SubjectOption[]; onChange: (next: { kind: TaskSubjectKind; id: string } | null) => void; className?: string; size?: "default" | "sm"; }) { const [open, setOpen] = useState(false); const chosen = value ? (options.find((option) => option.kind === value.kind && option.id === value.id) ?? null) : null; const Icon = chosen ? ICON[chosen.kind] : null; const groups = ORDER.map((kind) => ({ kind, rows: options.filter((option) => option.kind === kind), })).filter((group) => group.rows.length > 0); return ( { onChange(null); setOpen(false); }} className="text-muted-foreground px-2 py-1.5" > About nothing {groups.map((group) => { const GroupIcon = ICON[group.kind]; return (
{group.rows.map((option) => ( { onChange({ kind: option.kind, id: option.id }); setOpen(false); }} className="px-2 py-1.5" > {option.label} {option.hint && ( · {option.hint} )} ))}
); })}
); } /** The five nullable ids a server action wants, from the one thing you picked. */ export function subjectColumns(value: { kind: TaskSubjectKind; id: string } | null) { return { applicationId: value?.kind === "application" ? value.id : null, companyId: value?.kind === "company" ? value.id : null, contactId: value?.kind === "contact" ? value.id : null, resumeId: value?.kind === "resume" ? value.id : null, roleId: value?.kind === "role" ? value.id : null, noteId: value?.kind === "note" ? value.id : null, }; }