Files
multica/packages/views/projects/components/project-picker.tsx
Jiayuan Zhang 89fd2ce96e refactor(views): reuse AssigneePicker in CreateIssueModal (#599)
* refactor(views): reuse AssigneePicker in CreateIssueModal

Replace the hand-rolled inline assignee Popover in CreateIssueModal with
the shared AssigneePicker component. This fixes missing features (private
agent permission checks, lock icon, disabled state, selection checkmark)
and ensures consistent behavior across all assignee dropdowns.

* refactor(views): consolidate all picker components across the codebase

Enhance shared pickers (StatusPicker, PriorityPicker, DueDatePicker,
ProjectPicker) with triggerRender, controlled open/onOpenChange, and
align props — matching the AssigneePicker API.

Replace inline implementations in:
- create-issue.tsx: Status, Priority, DueDate, Project (4 pickers)
- issue-detail.tsx sidebar: Status, Priority (2 pickers)
- batch-action-toolbar.tsx: Status, Priority (2 pickers)

StatusPicker now has its first consumer (was defined but unused).
Removes ~200 lines of duplicated picker code.
2026-04-10 02:18:49 +08:00

62 lines
2.2 KiB
TypeScript

"use client";
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 {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuSeparator,
} from "@multica/ui/components/ui/dropdown-menu";
export function ProjectPicker({
projectId,
onUpdate,
triggerRender,
align = "start",
}: {
projectId: string | null;
onUpdate: (updates: Partial<UpdateIssueRequest>) => void;
triggerRender?: React.ReactElement;
align?: "start" | "center" | "end";
}) {
const wsId = useWorkspaceId();
const { data: projects = [] } = useQuery(projectListOptions(wsId));
const current = projects.find((p) => p.id === projectId);
return (
<DropdownMenu>
<DropdownMenuTrigger
className={triggerRender ? undefined : "flex items-center gap-1.5 cursor-pointer rounded px-1 -mx-1 hover:bg-accent/30 transition-colors overflow-hidden"}
render={triggerRender}
>
<FolderKanban className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
<span className="truncate">{current ? current.title : "No project"}</span>
</DropdownMenuTrigger>
<DropdownMenuContent align={align} className="w-52">
{projects.map((p) => (
<DropdownMenuItem key={p.id} onClick={() => onUpdate({ project_id: p.id })}>
<span className="mr-1">{p.icon || "📁"}</span>
<span className="truncate">{p.title}</span>
{p.id === projectId && <Check className="ml-auto h-3.5 w-3.5 shrink-0" />}
</DropdownMenuItem>
))}
{projects.length > 0 && projectId && <DropdownMenuSeparator />}
{projectId && (
<DropdownMenuItem onClick={() => onUpdate({ project_id: null })}>
<X className="h-3.5 w-3.5 text-muted-foreground" />
Remove from project
</DropdownMenuItem>
)}
{projects.length === 0 && (
<div className="px-2 py-1.5 text-xs text-muted-foreground">No projects yet</div>
)}
</DropdownMenuContent>
</DropdownMenu>
);
}