From 32119f5c2f10d5bfb90404484213d05af873e44f Mon Sep 17 00:00:00 2001 From: Forgotten Date: Fri, 20 Feb 2026 09:01:28 -0600 Subject: [PATCH] Add interactive project picker to issue detail page The project field in the issue header was a static read-only link (or invisible when unset), making it impossible to add/edit a project directly from the /issues/{id} page. Replace it with a searchable popover picker that always shows, matching the pattern used by StatusIcon and PriorityIcon. Co-Authored-By: Claude Sonnet 4.6 --- ui/src/pages/IssueDetail.tsx | 62 ++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/ui/src/pages/IssueDetail.tsx b/ui/src/pages/IssueDetail.tsx index 496ea847..37af1c4b 100644 --- a/ui/src/pages/IssueDetail.tsx +++ b/ui/src/pages/IssueDetail.tsx @@ -4,11 +4,12 @@ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { issuesApi } from "../api/issues"; import { activityApi } from "../api/activity"; import { agentsApi } from "../api/agents"; +import { projectsApi } from "../api/projects"; import { useCompany } from "../context/CompanyContext"; import { usePanel } from "../context/PanelContext"; import { useBreadcrumbs } from "../context/BreadcrumbContext"; import { queryKeys } from "../lib/queryKeys"; -import { relativeTime } from "../lib/utils"; +import { relativeTime, cn } from "../lib/utils"; import { InlineEditor } from "../components/InlineEditor"; import { CommentThread } from "../components/CommentThread"; import { IssueProperties } from "../components/IssueProperties"; @@ -20,7 +21,7 @@ import { Identity } from "../components/Identity"; import { Separator } from "@/components/ui/separator"; import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover"; import { Button } from "@/components/ui/button"; -import { ChevronRight, MoreHorizontal, EyeOff } from "lucide-react"; +import { ChevronRight, MoreHorizontal, EyeOff, Hexagon } from "lucide-react"; import type { ActivityEvent } from "@paperclip/shared"; import type { Agent } from "@paperclip/shared"; @@ -98,6 +99,8 @@ export function IssueDetail() { const queryClient = useQueryClient(); const navigate = useNavigate(); const [moreOpen, setMoreOpen] = useState(false); + const [projectOpen, setProjectOpen] = useState(false); + const [projectSearch, setProjectSearch] = useState(""); const { data: issue, isLoading, error } = useQuery({ queryKey: queryKeys.issues.detail(issueId!), @@ -136,6 +139,12 @@ export function IssueDetail() { enabled: !!selectedCompanyId, }); + const { data: projects } = useQuery({ + queryKey: queryKeys.projects.list(selectedCompanyId!), + queryFn: () => projectsApi.list(selectedCompanyId!), + enabled: !!selectedCompanyId, + }); + const agentMap = useMemo(() => { const map = new Map(); for (const a of agents ?? []) map.set(a.id, a); @@ -254,6 +263,55 @@ export function IssueDetail() { /> {issue.identifier ?? issue.id.slice(0, 8)} + { setProjectOpen(open); if (!open) setProjectSearch(""); }}> + + + + + setProjectSearch(e.target.value)} + autoFocus + /> + + {(projects ?? []) + .filter((p) => { + if (!projectSearch.trim()) return true; + return p.name.toLowerCase().includes(projectSearch.toLowerCase()); + }) + .map((p) => ( + + )) + } + + +