"use client"; import { useState } from "react"; import { Check, FolderKanban, X } from "lucide-react"; import { useQuery } from "@tanstack/react-query"; import { projectListOptions } from "@multica/core/projects/queries"; import { useWorkspaceId } from "@multica/core/hooks"; import type { UpdateIssueRequest } from "@multica/core/types"; import { cn } from "@multica/ui/lib/utils"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator, } from "@multica/ui/components/ui/dropdown-menu"; import { ProjectIcon } from "./project-icon"; import { useT } from "../../i18n"; export function ProjectPicker({ projectId, onUpdate, triggerRender, align = "start", defaultOpen = false, open: controlledOpen, onOpenChange, disabled = false, }: { projectId: string | null; onUpdate: (updates: Partial) => void; triggerRender?: React.ReactElement; align?: "start" | "center" | "end"; /** Open the dropdown on first mount. Used by progressive-disclosure * sidebars so a newly-added field immediately enters edit state. */ defaultOpen?: boolean; open?: boolean; onOpenChange?: (open: boolean) => void; /** Read-only lock. When true the trigger, the menu, and the inline clear * button are all disabled and out of the tab order, so no project-context * mutation can fire — pointer OR keyboard. Callers that must freeze the * selection during a transient window (an in-flight chat send) pass this; * every other caller keeps the default hover/keyboard clear behavior since * it defaults to false. */ disabled?: boolean; }) { const { t } = useT("projects"); const wsId = useWorkspaceId(); const { data: projects = [] } = useQuery(projectListOptions(wsId)); const current = projects.find((p) => p.id === projectId); // Normalize to an always-boolean controlled `open`, matching the other // pickers (status/priority/assignee/labels). Base UI's Menu latches a // controlled `open={true}` — a later `undefined` does NOT close it — so // callers wiring `open={cond ? true : undefined}` (create-issue dialog) // would leave the popup stuck open after selecting a project. const [internalOpen, setInternalOpen] = useState(defaultOpen); // A disabled picker can never be open, and no interaction may reopen it. const open = disabled ? false : controlledOpen ?? internalOpen; const setOpen = disabled ? () => {} : onOpenChange ?? setInternalOpen; return (
{current ? ( ) : ( )} {current ? current.title : t(($) => $.picker.no_project)} {current && ( )}
{projects.map((p) => ( onUpdate({ project_id: p.id })}> {p.title} {p.id === projectId && } ))} {projects.length > 0 && projectId && } {projectId && ( onUpdate({ project_id: null })}> {t(($) => $.picker.remove)} )} {projects.length === 0 && (
{t(($) => $.picker.empty)}
)}
); }