From f3d153c77ec142d181fda3135398a073c0b0c4bb Mon Sep 17 00:00:00 2001 From: Forgotten Date: Thu, 26 Feb 2026 16:15:23 -0600 Subject: [PATCH] fix(ui): move live badge to left of assignee in issues list The live badge was positioned after the assignee column, pushing the date column out of alignment on rows with live agents. Move it before the assignee so it doesn't displace the layout. Co-Authored-By: Claude Opus 4.6 --- ui/src/components/IssuesList.tsx | 71 ++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/ui/src/components/IssuesList.tsx b/ui/src/components/IssuesList.tsx index 79182857..3d57bcd7 100644 --- a/ui/src/components/IssuesList.tsx +++ b/ui/src/components/IssuesList.tsx @@ -12,10 +12,11 @@ import { PriorityIcon } from "./PriorityIcon"; import { EmptyState } from "./EmptyState"; import { Identity } from "./Identity"; import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover"; import { Checkbox } from "@/components/ui/checkbox"; import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@/components/ui/collapsible"; -import { CircleDot, Plus, Filter, ArrowUpDown, Layers, Check, X, ChevronRight, List, Columns3, User } from "lucide-react"; +import { CircleDot, Plus, Filter, ArrowUpDown, Layers, Check, X, ChevronRight, List, Columns3, User, Search } from "lucide-react"; import { KanbanBoard } from "./KanbanBoard"; import type { Issue } from "@paperclip/shared"; @@ -93,6 +94,25 @@ function applyFilters(issues: Issue[], state: IssueViewState): Issue[] { return result; } +function applySearch(issues: Issue[], searchQuery: string, agentName: (id: string | null) => string | null): Issue[] { + const query = searchQuery.trim().toLowerCase(); + if (!query) return issues; + + return issues.filter((issue) => { + const fields = [ + issue.identifier ?? "", + issue.title, + issue.description ?? "", + issue.status, + issue.priority, + agentName(issue.assigneeAgentId) ?? "", + ...(issue.labels ?? []).map((label) => label.name), + ]; + + return fields.some((field) => field.toLowerCase().includes(query)); + }); +} + function sortIssues(issues: Issue[], state: IssueViewState): Issue[] { const sorted = [...issues]; const dir = state.sortDir === "asc" ? 1 : -1; @@ -165,6 +185,7 @@ export function IssuesList({ }); const [assigneePickerIssueId, setAssigneePickerIssueId] = useState(null); const [assigneeSearch, setAssigneeSearch] = useState(""); + const [issueSearch, setIssueSearch] = useState(""); const updateView = useCallback((patch: Partial) => { setViewState((prev) => { @@ -180,8 +201,10 @@ export function IssuesList({ }; const filtered = useMemo(() => { - return sortIssues(applyFilters(issues, viewState), viewState); - }, [issues, viewState]); + const filteredByControls = applyFilters(issues, viewState); + const filteredBySearch = applySearch(filteredByControls, issueSearch, agentName); + return sortIssues(filteredBySearch, viewState); + }, [issues, viewState, issueSearch, agents]); const { data: labels } = useQuery({ queryKey: queryKeys.issues.labels(selectedCompanyId!), @@ -237,10 +260,22 @@ export function IssuesList({
{/* Toolbar */}
- +
+ +
+ + setIssueSearch(e.target.value)} + placeholder="Search issues..." + className="h-8 pl-7 text-xs sm:text-sm" + aria-label="Search issues" + /> +
+
{/* View mode toggle */} @@ -264,7 +299,7 @@ export function IssuesList({ {/* Filter */} -
)}
+ {liveIssueIds?.has(issue.id) && ( + + + + + + Live + + )}
- {liveIssueIds?.has(issue.id) && ( - - - - - - Live - - )} {formatDate(issue.createdAt)}