import { useState } from "react"; import type { Agent, Issue } from "@paperclipai/shared"; import { formatAssigneeUserLabel } from "../lib/assignees"; import { sortAgentsByRecency, getRecentAssigneeIds } from "../lib/recent-assignees"; import { buildExecutionPolicy, stageParticipantValues, } from "../lib/issue-execution-policy"; import { cn } from "../lib/utils"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { User, Eye, ShieldCheck } from "lucide-react"; import { AgentIcon } from "./AgentIconPicker"; type StageType = "review" | "approval"; interface ExecutionParticipantPickerProps { issue: Issue; stageType: StageType; agents: Agent[]; currentUserId: string | null; onUpdate: (data: Record) => void; } export function ExecutionParticipantPicker({ issue, stageType, agents, currentUserId, onUpdate, }: ExecutionParticipantPickerProps) { const [open, setOpen] = useState(false); const [search, setSearch] = useState(""); const reviewerValues = stageParticipantValues(issue.executionPolicy, "review"); const approverValues = stageParticipantValues(issue.executionPolicy, "approval"); const values = stageType === "review" ? reviewerValues : approverValues; const sortedAgents = sortAgentsByRecency( agents.filter((a) => a.status !== "terminated"), getRecentAssigneeIds(), ); const userLabel = (userId: string | null | undefined) => formatAssigneeUserLabel(userId, currentUserId); const creatorUserLabel = userLabel(issue.createdByUserId); const agentName = (id: string) => { const agent = agents.find((a) => a.id === id); return agent?.name ?? id.slice(0, 8); }; const participantLabel = (value: string) => { if (value.startsWith("agent:")) return agentName(value.slice("agent:".length)); if (value.startsWith("user:")) return userLabel(value.slice("user:".length)) ?? "User"; return value; }; const updatePolicy = (nextValues: string[]) => { onUpdate({ executionPolicy: buildExecutionPolicy({ existingPolicy: issue.executionPolicy ?? null, reviewerValues: stageType === "review" ? nextValues : reviewerValues, approverValues: stageType === "approval" ? nextValues : approverValues, }), }); }; const toggle = (value: string) => { const next = values.includes(value) ? values.filter((v) => v !== value) : [...values, value]; updatePolicy(next); }; const label = stageType === "review" ? "Reviewers" : "Approvers"; const Icon = stageType === "review" ? Eye : ShieldCheck; return ( { setOpen(o); if (!o) setSearch(""); }}> setSearch(e.target.value)} autoFocus />
{currentUserId && ( )} {issue.createdByUserId && issue.createdByUserId !== currentUserId && ( )} {sortedAgents .filter((agent) => { if (!search.trim()) return true; return agent.name.toLowerCase().includes(search.toLowerCase()); }) .map((agent) => { const encoded = `agent:${agent.id}`; return ( ); })}
); }