Skip to content

Commit beff4c3

Browse files
committed
sig: add frontend support for filtering by suggested reviewer
1 parent f32b156 commit beff4c3

14 files changed

+1128
-269
lines changed

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

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type {
2+
AvailableSuggestedReviewer,
3+
AvailableSuggestedReviewersResponse,
24
SandboxEnvironment,
35
SandboxEnvironmentInput,
46
SignalReport,
@@ -161,6 +163,50 @@ function parseSignalReportArtefactsPayload(
161163
};
162164
}
163165

166+
function normalizeAvailableSuggestedReviewer(
167+
uuid: string,
168+
value: unknown,
169+
): AvailableSuggestedReviewer | null {
170+
if (!isObjectRecord(value)) {
171+
return null;
172+
}
173+
174+
const normalizedUuid = optionalString(uuid);
175+
if (!normalizedUuid) {
176+
return null;
177+
}
178+
179+
return {
180+
uuid: normalizedUuid,
181+
name: optionalString(value.name) ?? "",
182+
email: optionalString(value.email) ?? "",
183+
};
184+
}
185+
186+
function parseAvailableSuggestedReviewersPayload(
187+
value: unknown,
188+
): AvailableSuggestedReviewersResponse {
189+
if (!isObjectRecord(value)) {
190+
return {
191+
results: [],
192+
count: 0,
193+
};
194+
}
195+
196+
const results = Object.entries(value)
197+
.map(([uuid, reviewer]) =>
198+
normalizeAvailableSuggestedReviewer(uuid, reviewer),
199+
)
200+
.filter(
201+
(reviewer): reviewer is AvailableSuggestedReviewer => reviewer !== null,
202+
);
203+
204+
return {
205+
results,
206+
count: results.length,
207+
};
208+
}
209+
164210
export class PostHogAPIClient {
165211
private api: ReturnType<typeof createApiClient>;
166212
private _teamId: number | null = null;
@@ -958,6 +1004,9 @@ export class PostHogAPIClient {
9581004
if (params?.source_product) {
9591005
url.searchParams.set("source_product", params.source_product);
9601006
}
1007+
if (params?.suggested_reviewers) {
1008+
url.searchParams.set("suggested_reviewers", params.suggested_reviewers);
1009+
}
9611010

9621011
const response = await this.api.fetcher.fetch({
9631012
method: "get",
@@ -976,6 +1025,34 @@ export class PostHogAPIClient {
9761025
};
9771026
}
9781027

1028+
async getAvailableSuggestedReviewers(
1029+
query?: string,
1030+
): Promise<AvailableSuggestedReviewersResponse> {
1031+
const teamId = await this.getTeamId();
1032+
const url = new URL(
1033+
`${this.api.baseUrl}/api/projects/${teamId}/signal_reports/available_reviewers/`,
1034+
);
1035+
const path = `/api/projects/${teamId}/signal_reports/available_reviewers/`;
1036+
1037+
if (query?.trim()) {
1038+
url.searchParams.set("query", query.trim());
1039+
}
1040+
1041+
const response = await this.api.fetcher.fetch({
1042+
method: "get",
1043+
url,
1044+
path,
1045+
});
1046+
1047+
if (!response.ok) {
1048+
throw new Error(
1049+
`Failed to fetch available suggested reviewers: ${response.statusText}`,
1050+
);
1051+
}
1052+
1053+
return parseAvailableSuggestedReviewersPayload(await response.json());
1054+
}
1055+
9791056
async getSignalReportSignals(
9801057
reportId: string,
9811058
): Promise<SignalReportSignalsResponse> {

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

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import {
66
} from "@features/inbox/components/InboxEmptyStates";
77
import { InboxLiveRail } from "@features/inbox/components/InboxLiveRail";
88
import { InboxSourcesDialog } from "@features/inbox/components/InboxSourcesDialog";
9-
import { useInboxReportsInfinite } from "@features/inbox/hooks/useInboxReports";
9+
import {
10+
useInboxAvailableSuggestedReviewers,
11+
useInboxReportsInfinite,
12+
} from "@features/inbox/hooks/useInboxReports";
1013
import { useSignalSourceConfigs } from "@features/inbox/hooks/useSignalSourceConfigs";
1114
import { useInboxReportSelectionStore } from "@features/inbox/stores/inboxReportSelectionStore";
1215
import { useInboxSignalsFilterStore } from "@features/inbox/stores/inboxSignalsFilterStore";
@@ -15,6 +18,7 @@ import { useInboxSourcesDialogStore } from "@features/inbox/stores/inboxSourcesD
1518
import {
1619
buildSignalReportListOrdering,
1720
buildStatusFilterParam,
21+
buildSuggestedReviewerFilterParam,
1822
filterReportsBySearch,
1923
} from "@features/inbox/utils/filterReports";
2024
import { INBOX_REFETCH_INTERVAL_MS } from "@features/inbox/utils/inboxConstants";
@@ -38,6 +42,9 @@ export function InboxSignalsTab() {
3842
const sourceProductFilter = useInboxSignalsFilterStore(
3943
(s) => s.sourceProductFilter,
4044
);
45+
const suggestedReviewerFilter = useInboxSignalsFilterStore(
46+
(s) => s.suggestedReviewerFilter,
47+
);
4148

4249
// ── Signal source configs ───────────────────────────────────────────────
4350
const { data: signalSourceConfigs } = useSignalSourceConfigs();
@@ -64,6 +71,10 @@ export function InboxSignalsTab() {
6471
const inboxPollingActive = windowFocused && isInboxView;
6572

6673
// ── Data fetching ───────────────────────────────────────────────────────
74+
useInboxAvailableSuggestedReviewers({
75+
enabled: isInboxView,
76+
});
77+
6778
const inboxQueryParams = useMemo(
6879
(): SignalReportsQueryParams => ({
6980
status: buildStatusFilterParam(statusFilter),
@@ -72,8 +83,18 @@ export function InboxSignalsTab() {
7283
sourceProductFilter.length > 0
7384
? sourceProductFilter.join(",")
7485
: undefined,
86+
suggested_reviewers:
87+
suggestedReviewerFilter.length > 0
88+
? buildSuggestedReviewerFilterParam(suggestedReviewerFilter)
89+
: undefined,
7590
}),
76-
[statusFilter, sortField, sortDirection, sourceProductFilter],
91+
[
92+
statusFilter,
93+
sortField,
94+
sortDirection,
95+
sourceProductFilter,
96+
suggestedReviewerFilter,
97+
],
7798
);
7899

79100
const {
@@ -194,7 +215,9 @@ export function InboxSignalsTab() {
194215
// ── Layout mode (computed early — needed by focus effect below) ────────
195216
const hasReports = allReports.length > 0;
196217
const hasActiveFilters =
197-
sourceProductFilter.length > 0 || statusFilter.length < 5;
218+
sourceProductFilter.length > 0 ||
219+
suggestedReviewerFilter.length > 0 ||
220+
statusFilter.length < 5;
198221
const shouldShowTwoPane =
199222
hasReports || !!searchQuery.trim() || hasActiveFilters;
200223

@@ -324,21 +347,15 @@ export function InboxSignalsTab() {
324347
className="outline-none"
325348
onMouseDownCapture={(e) => {
326349
const target = e.target as HTMLElement;
327-
if (
328-
target.closest(
329-
"[data-report-id], button, input, select, textarea, [role='checkbox']",
330-
)
331-
) {
350+
if (target.closest("[data-report-id]")) {
332351
focusListPane();
333352
}
334353
}}
335354
onFocusCapture={(e) => {
336355
const target = e.target as HTMLElement;
337356
if (
338357
target !== leftPaneRef.current &&
339-
target.closest(
340-
"[data-report-id], button, input, select, textarea, [role='checkbox']",
341-
)
358+
target.closest("[data-report-id]")
342359
) {
343360
focusListPane();
344361
}

0 commit comments

Comments
 (0)