From 5d925713132ab9ff9e03e818241b104b0f60dd47 Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 5 May 2026 10:52:40 -0500 Subject: [PATCH] Strip blue link styling, status badge, project chip from search rows (PAP-3468) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue rows on /search now drop the right-side StatusBadge and project chip — status is already conveyed by the StatusIcon on the left, and the project hexagon ("Board UI") was visual noise. The Link wrapper picks up the default browser styling (blue underline through children); explicitly setting no-underline + text-inherit removes the stray blue line. Wrapped the row in React.memo and dropped the now-unused projects fetch from Search.tsx. Co-Authored-By: Claude Opus 4.7 --- ui/src/components/search/SearchResultRow.tsx | 46 +++++--------------- ui/src/pages/Search.tsx | 20 +-------- ui/storybook/stories/search.stories.tsx | 3 -- 3 files changed, 13 insertions(+), 56 deletions(-) diff --git a/ui/src/components/search/SearchResultRow.tsx b/ui/src/components/search/SearchResultRow.tsx index dc80855d04f..a108aff30b8 100644 --- a/ui/src/components/search/SearchResultRow.tsx +++ b/ui/src/components/search/SearchResultRow.tsx @@ -1,10 +1,9 @@ -import type { ComponentType, SVGProps } from "react"; +import { memo, type ComponentType, type SVGProps } from "react"; import { Bot, FileText, Hexagon, MessageSquare, Quote } from "lucide-react"; -import type { Agent, CompanySearchResult, Project } from "@paperclipai/shared"; +import type { Agent, CompanySearchResult } from "@paperclipai/shared"; import { Link } from "@/lib/router"; import { cn } from "@/lib/utils"; import { StatusIcon } from "../StatusIcon"; -import { StatusBadge } from "../StatusBadge"; import { Identity } from "../Identity"; import { HighlightedText, type HighlightedTextProps } from "./HighlightedText"; @@ -47,15 +46,16 @@ function formatRelativeTime(input: string | null): string { export interface SearchResultRowProps { result: CompanySearchResult; agentsById?: ReadonlyMap>; - projectsById?: ReadonlyMap>; isActive?: boolean; className?: string; } -export function SearchResultRow({ +const ROW_BASE = + "group flex items-start gap-3 px-4 transition-colors no-underline text-inherit hover:bg-muted/40"; + +function SearchResultRowImpl({ result, agentsById, - projectsById, isActive, className, }: SearchResultRowProps) { @@ -63,11 +63,7 @@ export function SearchResultRow({ return ( @@ -94,11 +90,7 @@ export function SearchResultRow({ return ( @@ -122,7 +114,6 @@ export function SearchResultRow({ const assigneeName = issue.assigneeAgentId ? agentsById?.get(issue.assigneeAgentId)?.name ?? null : null; - const projectName = issue.projectId ? projectsById?.get(issue.projectId)?.name ?? null : null; const updated = formatRelativeTime(result.updatedAt ?? issue.updatedAt); const titleHighlights = result.snippets.find((snippet) => snippet.field === "title")?.highlights; const bodySnippets = result.snippets.filter((snippet) => snippet.field !== "title").slice(0, 2); @@ -131,11 +122,7 @@ export function SearchResultRow({
@@ -154,17 +141,8 @@ export function SearchResultRow({ className="line-clamp-2 min-w-0 flex-1 text-sm font-medium leading-snug text-foreground" />
- {assigneeName ? : null} - {projectName ? ( - - - {projectName} - - ) : null} - {updated ? ( - {updated} - ) : null} + {updated ? {updated} : null}
{bodySnippets.map((snippet, index) => ( @@ -178,9 +156,7 @@ export function SearchResultRow({ /> ))}
- {assigneeName ? {assigneeName} : null} - {projectName ? · {projectName} : null} {updated ? {updated} : null}
@@ -188,6 +164,8 @@ export function SearchResultRow({ ); } +export const SearchResultRow = memo(SearchResultRowImpl); + interface SnippetLineProps { text: string; highlights?: HighlightedTextProps["highlights"]; diff --git a/ui/src/pages/Search.tsx b/ui/src/pages/Search.tsx index 1331345a56a..149f2bbbc58 100644 --- a/ui/src/pages/Search.tsx +++ b/ui/src/pages/Search.tsx @@ -20,13 +20,12 @@ import { useBreadcrumbs } from "../context/BreadcrumbContext"; import { useDialogActions } from "../context/DialogContext"; import { searchApi } from "../api/search"; import { agentsApi } from "../api/agents"; -import { projectsApi } from "../api/projects"; import { queryKeys } from "../lib/queryKeys"; import { loadRecentSearches, pushRecentSearch } from "../lib/recent-searches"; import { PageTabBar, type PageTabItem } from "../components/PageTabBar"; import { IssueGroupHeader } from "../components/IssueGroupHeader"; import { SearchResultRow } from "../components/search/SearchResultRow"; -import type { Agent, Project } from "@paperclipai/shared"; +import type { Agent } from "@paperclipai/shared"; const SEARCH_DEBOUNCE_MS = 250; const IDENTIFIER_PATTERN = /^[A-Z]+-\d+$/; @@ -202,24 +201,12 @@ export function Search() { enabled: !!selectedCompanyId, }); - const { data: projects } = useQuery({ - queryKey: queryKeys.projects.list(selectedCompanyId!), - queryFn: () => projectsApi.list(selectedCompanyId!), - enabled: !!selectedCompanyId, - }); - const agentsById = useMemo>>(() => { const map = new Map>(); for (const agent of agents ?? []) map.set(agent.id, agent); return map; }, [agents]); - const projectsById = useMemo>>(() => { - const map = new Map>(); - for (const project of projects ?? []) map.set(project.id, project); - return map; - }, [projects]); - // Persist recent searches once we have a successful response with a non-empty query. useEffect(() => { if (!selectedCompanyId) return; @@ -409,7 +396,6 @@ export function Search() { totalResults={totalResults} isFetching={isFetching && !!data} agentsById={agentsById} - projectsById={projectsById} /> ) : null} @@ -438,7 +424,6 @@ interface SearchTabContentProps { totalResults: number; isFetching: boolean; agentsById: ReadonlyMap>; - projectsById: ReadonlyMap>; } function SearchTabContent({ @@ -460,7 +445,6 @@ function SearchTabContent({ totalResults, isFetching, agentsById, - projectsById, }: SearchTabContentProps) { if (showInitialState) { return ( @@ -623,7 +607,6 @@ function SearchTabContent({ key={`${result.type}:${result.id}:${result.href}`} result={result} agentsById={agentsById} - projectsById={projectsById} /> ))} @@ -638,7 +621,6 @@ function SearchTabContent({ key={`${result.type}:${result.id}:${result.href}`} result={result} agentsById={agentsById} - projectsById={projectsById} /> ))} diff --git a/ui/storybook/stories/search.stories.tsx b/ui/storybook/stories/search.stories.tsx index 3bf2507cdc7..f3abd9642ab 100644 --- a/ui/storybook/stories/search.stories.tsx +++ b/ui/storybook/stories/search.stories.tsx @@ -33,7 +33,6 @@ import { StatusBadge } from "@/components/StatusBadge"; import { storybookAgents, storybookProjects, storybookIssues } from "../fixtures/paperclipData"; const agentsById = new Map(storybookAgents.map((agent) => [agent.id, agent])); -const projectsById = new Map(storybookProjects.map((project) => [project.id, project])); type IssueResultOverrides = Omit, "issue"> & { issue?: Partial>; @@ -282,7 +281,6 @@ function SearchPagePreview({ key={result.id} result={result} agentsById={agentsById} - projectsById={projectsById} /> ))} @@ -580,7 +578,6 @@ function SearchStories() { key={result.id} result={result} agentsById={agentsById} - projectsById={projectsById} /> ))} {fixtureAgents.map((result) => (