From cb6e6151861e3676ef5a3842ba888a5959779222 Mon Sep 17 00:00:00 2001 From: dotta Date: Mon, 6 Apr 2026 19:14:40 -0500 Subject: [PATCH] Revert reviewer/approver pickers to sidebar, add to new-issue chip bar Per feedback: reviewer/approver pickers were incorrectly placed in the issue header row. This moves them back to the Properties sidebar at regular size and adds them as small chip-style selectors in the new-issue dialog's bottom bar (next to Upload), matching the existing chip styling. - Restored Reviewers/Approvers PropertyPicker rows in IssueProperties - Removed ExecutionParticipantPicker pills from IssueDetail header - Added Eye/ShieldCheck-icon reviewer/approver InlineEntitySelectors in NewIssueDialog chip bar after Upload button Co-Authored-By: Paperclip --- ui/src/components/IssueProperties.tsx | 111 +++++++++++++++++++++ ui/src/components/NewIssueDialog.tsx | 136 +++++++++++++++----------- ui/src/pages/IssueDetail.tsx | 18 ---- 3 files changed, 188 insertions(+), 77 deletions(-) diff --git a/ui/src/components/IssueProperties.tsx b/ui/src/components/IssueProperties.tsx index 9f882ef8..937e4628 100644 --- a/ui/src/components/IssueProperties.tsx +++ b/ui/src/components/IssueProperties.tsx @@ -12,6 +12,7 @@ import { queryKeys } from "../lib/queryKeys"; import { useProjectOrder } from "../hooks/useProjectOrder"; import { getRecentAssigneeIds, sortAgentsByRecency, trackRecentAssignee } from "../lib/recent-assignees"; import { formatAssigneeUserLabel } from "../lib/assignees"; +import { buildExecutionPolicy, stageParticipantValues } from "../lib/issue-execution-policy"; import { StatusIcon } from "./StatusIcon"; import { PriorityIcon } from "./PriorityIcon"; import { Identity } from "./Identity"; @@ -269,9 +270,45 @@ export function IssueProperties({ const assignee = issue.assigneeAgentId ? agents?.find((a) => a.id === issue.assigneeAgentId) : null; + const reviewerValues = stageParticipantValues(issue.executionPolicy, "review"); + const approverValues = stageParticipantValues(issue.executionPolicy, "approval"); const userLabel = (userId: string | null | undefined) => formatAssigneeUserLabel(userId, currentUserId); const assigneeUserLabel = userLabel(issue.assigneeUserId); const creatorUserLabel = userLabel(issue.createdByUserId); + const updateExecutionPolicy = (nextReviewers: string[], nextApprovers: string[]) => { + onUpdate({ + executionPolicy: buildExecutionPolicy({ + existingPolicy: issue.executionPolicy ?? null, + reviewerValues: nextReviewers, + approverValues: nextApprovers, + }), + }); + }; + const toggleExecutionParticipant = (stageType: "review" | "approval", value: string) => { + const currentValues = stageType === "review" ? reviewerValues : approverValues; + const nextValues = currentValues.includes(value) + ? currentValues.filter((candidate) => candidate !== value) + : [...currentValues, value]; + updateExecutionPolicy( + stageType === "review" ? nextValues : reviewerValues, + stageType === "approval" ? nextValues : approverValues, + ); + }; + const executionParticipantLabel = (value: string) => { + if (value.startsWith("agent:")) { + return agentName(value.slice("agent:".length)) ?? value.slice("agent:".length, "agent:".length + 8); + } + if (value.startsWith("user:")) { + return userLabel(value.slice("user:".length)) ?? "User"; + } + return value; + }; + const reviewerTrigger = reviewerValues.length > 0 + ? {reviewerValues.map((value) => executionParticipantLabel(value)).join(", ")} + : None; + const approverTrigger = approverValues.length > 0 + ? {approverValues.map((value) => executionParticipantLabel(value)).join(", ")} + : None; const currentExecutionLabel = (() => { if (!issue.executionState?.currentStageType) return null; const stageLabel = issue.executionState.currentStageType === "review" ? "Review" : "Approval"; @@ -472,6 +509,80 @@ export function IssueProperties({ ); + const executionParticipantsContent = ( + stageType: "review" | "approval", + values: string[], + search: string, + setSearch: (value: string) => void, + onClear: () => void, + ) => ( + <> + setSearch(e.target.value)} + autoFocus={!inline} + /> +
+ + {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 ( + + ); + })} +
+ + ); + const projectTrigger = issue.projectId ? ( <> - - option ? ( - {`Reviewer: ${option.label}`} - ) : ( - Reviewer - ) - } - renderOption={(option) => { - if (!option.id) return {option.label}; - const reviewer = parseAssigneeValue(option.id).assigneeAgentId - ? (agents ?? []).find((agent) => agent.id === parseAssigneeValue(option.id).assigneeAgentId) - : null; - return ( - <> - {reviewer ? : null} - {option.label} - - ); - }} - /> - - option ? ( - {`Approver: ${option.label}`} - ) : ( - Approver - ) - } - renderOption={(option) => { - if (!option.id) return {option.label}; - const approver = parseAssigneeValue(option.id).assigneeAgentId - ? (agents ?? []).find((agent) => agent.id === parseAssigneeValue(option.id).assigneeAgentId) - : null; - return ( - <> - {approver ? : null} - {option.label} - - ); - }} - /> @@ -1538,7 +1482,7 @@ export function NewIssueDialog() { multiple /> + + option ? ( + <> + + {option.label} + + ) : ( + <> + + Reviewer + + ) + } + renderOption={(option) => { + if (!option.id) return {option.label}; + const reviewer = parseAssigneeValue(option.id).assigneeAgentId + ? (agents ?? []).find((agent) => agent.id === parseAssigneeValue(option.id).assigneeAgentId) + : null; + return ( + <> + {reviewer ? : null} + {option.label} + + ); + }} + /> + + + option ? ( + <> + + {option.label} + + ) : ( + <> + + Approver + + ) + } + renderOption={(option) => { + if (!option.id) return {option.label}; + const approver = parseAssigneeValue(option.id).assigneeAgentId + ? (agents ?? []).find((agent) => agent.id === parseAssigneeValue(option.id).assigneeAgentId) + : null; + return ( + <> + {approver ? : null} + {option.label} + + ); + }} + /> + {/* More (dates) */} diff --git a/ui/src/pages/IssueDetail.tsx b/ui/src/pages/IssueDetail.tsx index 8b11d5de..05fb5af8 100644 --- a/ui/src/pages/IssueDetail.tsx +++ b/ui/src/pages/IssueDetail.tsx @@ -43,7 +43,6 @@ import { InlineEditor } from "../components/InlineEditor"; import { CommentThread } from "../components/CommentThread"; import { IssueDocumentsSection } from "../components/IssueDocumentsSection"; import { IssueProperties } from "../components/IssueProperties"; -import { ExecutionParticipantPicker } from "../components/ExecutionParticipantPicker"; import { IssueWorkspaceCard } from "../components/IssueWorkspaceCard"; import { LiveRunWidget } from "../components/LiveRunWidget"; import type { MentionOption } from "../components/MarkdownEditor"; @@ -1356,23 +1355,6 @@ export function IssueDetail() { )} -
- updateIssue.mutate(data)} - /> - updateIssue.mutate(data)} - /> -
-