Skip to content

Commit deb2ce4

Browse files
committed
feat(inbox): filter reports by signal source product
- Add source_product query param to SignalReportsQueryParams and API client - Add sourceProductFilter to filter store (empty = all sources, persisted) - Add "Source" multi-select section to toolbar filter/sort popover - Pass source_product filter to API when any sources are selected - Supports: session_replay, error_tracking, llm_analytics, github, linear, zendesk
1 parent 84cdcd4 commit deb2ce4

File tree

7 files changed

+228
-42
lines changed

7 files changed

+228
-42
lines changed

apps/code/src/renderer/api/posthogClient.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,9 @@ export class PostHogAPIClient {
946946
if (params?.ordering) {
947947
url.searchParams.set("ordering", params.ordering);
948948
}
949+
if (params?.source_product) {
950+
url.searchParams.set("source_product", params.source_product);
951+
}
949952

950953
const response = await this.api.fetcher.fetch({
951954
method: "get",

apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ export function InboxSignalsTab() {
299299
const sortDirection = useInboxSignalsFilterStore((s) => s.sortDirection);
300300
const searchQuery = useInboxSignalsFilterStore((s) => s.searchQuery);
301301
const statusFilter = useInboxSignalsFilterStore((s) => s.statusFilter);
302+
const sourceProductFilter = useInboxSignalsFilterStore(
303+
(s) => s.sourceProductFilter,
304+
);
302305
const { data: signalSourceConfigs } = useSignalSourceConfigs();
303306
const hasSignalSources = signalSourceConfigs?.some((c) => c.enabled) ?? false;
304307
const [sourcesDialogOpen, setSourcesDialogOpen] = useState(false);
@@ -311,8 +314,12 @@ export function InboxSignalsTab() {
311314
(): SignalReportsQueryParams => ({
312315
status: buildStatusFilterParam(statusFilter),
313316
ordering: buildSignalReportListOrdering(sortField, sortDirection),
317+
source_product:
318+
sourceProductFilter.length > 0
319+
? sourceProductFilter.join(",")
320+
: undefined,
314321
}),
315-
[statusFilter, sortField, sortDirection],
322+
[statusFilter, sortField, sortDirection, sourceProductFilter],
316323
);
317324

318325
const {
@@ -605,6 +612,7 @@ export function InboxSignalsTab() {
605612
content={selectedReport.summary}
606613
fallback="No summary available."
607614
variant="detail"
615+
pending={selectedReport.status !== "ready"}
608616
/>
609617
<Flex align="center" gap="2" wrap="wrap">
610618
<SignalReportPriorityBadge priority={selectedReport.priority} />
@@ -628,7 +636,12 @@ export function InboxSignalsTab() {
628636
</Text>
629637
<Flex direction="column" gap="1">
630638
{suggestedReviewers.map((reviewer) => (
631-
<Flex key={reviewer.github_login} align="center" gap="2">
639+
<Flex
640+
key={reviewer.github_login}
641+
align="center"
642+
gap="2"
643+
wrap="wrap"
644+
>
632645
<GithubLogoIcon
633646
size={14}
634647
className="shrink-0 text-gray-10"
@@ -642,10 +655,30 @@ export function InboxSignalsTab() {
642655
href={`https://github.com/${reviewer.github_login}`}
643656
target="_blank"
644657
rel="noreferrer"
645-
className="text-[11px] text-gray-9 hover:text-gray-11"
658+
className="inline-flex items-center gap-0.5 text-[11px] text-gray-9 hover:text-gray-11"
646659
>
647660
@{reviewer.github_login}
661+
<ArrowSquareOutIcon size={10} />
648662
</a>
663+
{reviewer.relevant_commits.length > 0 && (
664+
<span className="text-[11px] text-gray-9">
665+
{reviewer.relevant_commits.map((commit, i) => (
666+
<span key={commit.sha}>
667+
{i > 0 && ", "}
668+
<Tooltip content={commit.reason || undefined}>
669+
<a
670+
href={commit.url}
671+
target="_blank"
672+
rel="noreferrer"
673+
className="font-mono text-gray-9 hover:text-gray-11"
674+
>
675+
{commit.sha.slice(0, 7)}
676+
</a>
677+
</Tooltip>
678+
</span>
679+
))}
680+
</span>
681+
)}
649682
</Flex>
650683
))}
651684
</Flex>

apps/code/src/renderer/features/inbox/components/ReportCard.tsx

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,32 @@ import {
44
inboxStatusAccentCss,
55
inboxStatusLabel,
66
} from "@features/inbox/utils/inboxSort";
7-
import { UserIcon } from "@phosphor-icons/react";
7+
import {
8+
BrainIcon,
9+
BugIcon,
10+
EyeIcon,
11+
GithubLogoIcon,
12+
KanbanIcon,
13+
TicketIcon,
14+
VideoIcon,
15+
} from "@phosphor-icons/react";
816
import { Flex, Text, Tooltip } from "@radix-ui/themes";
917
import type { SignalReport } from "@shared/types";
1018
import { motion } from "framer-motion";
1119
import type { KeyboardEvent, MouseEvent } from "react";
1220

21+
const SOURCE_PRODUCT_ICONS: Record<
22+
string,
23+
{ icon: React.ReactNode; color: string }
24+
> = {
25+
session_replay: { icon: <VideoIcon size={12} />, color: "var(--amber-9)" },
26+
error_tracking: { icon: <BugIcon size={12} />, color: "var(--red-9)" },
27+
llm_analytics: { icon: <BrainIcon size={12} />, color: "var(--purple-9)" },
28+
github: { icon: <GithubLogoIcon size={12} />, color: "var(--gray-11)" },
29+
linear: { icon: <KanbanIcon size={12} />, color: "var(--blue-9)" },
30+
zendesk: { icon: <TicketIcon size={12} />, color: "var(--green-9)" },
31+
};
32+
1333
interface ReportCardProps {
1434
report: SignalReport;
1535
isSelected: boolean;
@@ -75,26 +95,49 @@ export function ReportCard({
7595
}}
7696
className="w-full cursor-pointer overflow-hidden border-gray-5 border-b py-2 pr-3 pl-2 text-left transition-colors hover:bg-gray-2"
7797
style={{
78-
backgroundColor: isSelected ? "var(--gray-3)" : "transparent",
98+
backgroundColor: isSelected
99+
? "var(--gray-3)"
100+
: report.is_suggested_reviewer
101+
? "var(--blue-2)"
102+
: "transparent",
79103
boxShadow: `inset 3px 0 0 0 ${accent}`,
80104
}}
81105
>
82106
<Flex align="start" justify="between" gap="3">
83107
<Flex direction="column" gap="1" style={{ minWidth: 0, flex: 1 }}>
84-
{/* Bullet stays in its own column so title + badges wrap under each other, not under the dot */}
85-
<Flex align="center" gapX="2" className="min-w-0">
86-
<span
87-
title={`Signal strength: ${strengthLabel}`}
88-
aria-hidden
108+
<Flex align="start" gapX="2" className="min-w-0">
109+
{/* Source product icons — 4px top padding centers 12px icons
110+
with the title's 13px/~20px effective line height */}
111+
<Flex
112+
direction="column"
113+
align="center"
114+
gap="0.5"
89115
className="shrink-0"
90-
style={{
91-
width: "6px",
92-
height: "6px",
93-
borderRadius: "9999px",
94-
backgroundColor: strengthColor,
95-
display: "inline-block",
96-
}}
97-
/>
116+
style={{ paddingTop: 4 }}
117+
>
118+
{(report.source_products ?? []).length > 0 ? (
119+
(report.source_products ?? []).map((sp) => {
120+
const info = SOURCE_PRODUCT_ICONS[sp];
121+
return info ? (
122+
<span key={sp} style={{ color: info.color }}>
123+
{info.icon}
124+
</span>
125+
) : null;
126+
})
127+
) : (
128+
<span
129+
title={`Signal strength: ${strengthLabel}`}
130+
aria-hidden
131+
style={{
132+
width: "6px",
133+
height: "6px",
134+
borderRadius: "9999px",
135+
backgroundColor: strengthColor,
136+
display: "inline-block",
137+
}}
138+
/>
139+
)}
140+
</Flex>
98141
<Flex
99142
align="center"
100143
gapX="2"
@@ -129,21 +172,22 @@ export function ReportCard({
129172
border: "1px solid var(--blue-6)",
130173
}}
131174
>
132-
<UserIcon size={10} weight="bold" />
175+
<EyeIcon size={10} weight="bold" />
133176
</span>
134177
</Tooltip>
135178
)}
136179
</Flex>
137180
</Flex>
138181
{/* Summary is outside the title row so wrapped lines align with title text (bullet + gap), not the card edge */}
139182
<div
140-
className="min-w-0 pl-[calc(6px+var(--space-2))]"
183+
className="min-w-0 pl-[calc(12px+var(--space-2))]"
141184
style={{ opacity: isReady ? 1 : 0.82 }}
142185
>
143186
<SignalReportSummaryMarkdown
144187
content={report.summary}
145188
fallback="No summary yet — still collecting context."
146189
variant="list"
190+
pending={!isReady}
147191
/>
148192
</div>
149193
</Flex>

apps/code/src/renderer/features/inbox/components/SignalReportSummaryMarkdown.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ interface SignalReportSummaryMarkdownProps {
77
fallback: string;
88
/** List rows: clamp lines and tighter spacing. Detail: full block markdown. */
99
variant: "list" | "detail";
10+
/** Render in italic to indicate the summary is still being written. */
11+
pending?: boolean;
1012
}
1113

1214
/**
@@ -16,14 +18,17 @@ export function SignalReportSummaryMarkdown({
1618
content,
1719
fallback,
1820
variant,
21+
pending,
1922
}: SignalReportSummaryMarkdownProps) {
2023
const raw = content?.trim() ? content : fallback;
2124

25+
const italicStyle = pending ? { fontStyle: "italic" as const } : undefined;
26+
2227
if (variant === "list") {
2328
return (
2429
<Box
2530
className="[&_.rt-Text]:!mb-0 [&_p]:!mb-0 [&_ul]:!mb-0 min-w-0 text-left [&_li]:mb-0"
26-
style={{ color: "var(--gray-11)" }}
31+
style={{ color: "var(--gray-11)", ...italicStyle }}
2732
>
2833
<div className="line-clamp-2 overflow-hidden text-[12px] leading-snug [&_a]:pointer-events-auto">
2934
<MarkdownRenderer content={raw} />
@@ -35,7 +40,7 @@ export function SignalReportSummaryMarkdown({
3540
return (
3641
<Box
3742
className="min-w-0 text-pretty break-words [&_.rt-Text]:mb-2 [&_li]:mb-1 [&_p:last-child]:mb-0"
38-
style={{ color: "var(--gray-11)" }}
43+
style={{ color: "var(--gray-11)", ...italicStyle }}
3944
>
4045
<div className="text-[12px] leading-relaxed [&_a]:pointer-events-auto">
4146
<MarkdownRenderer content={raw} />

0 commit comments

Comments
 (0)