Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/code/src/renderer/api/posthogClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1898,6 +1898,9 @@ export class PostHogAPIClient {
if (params?.suggested_reviewers) {
url.searchParams.set("suggested_reviewers", params.suggested_reviewers);
}
if (params?.priority) {
url.searchParams.set("priority", params.priority);
}

const response = await this.api.fetcher.fetch({
method: "get",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useInboxSignalsFilterStore } from "@features/inbox/stores/inboxSignalsF
import { useInboxSignalsSidebarStore } from "@features/inbox/stores/inboxSignalsSidebarStore";
import { useInboxSourcesDialogStore } from "@features/inbox/stores/inboxSourcesDialogStore";
import {
buildPriorityFilterParam,
buildSignalReportListOrdering,
buildStatusFilterParam,
buildSuggestedReviewerFilterParam,
Expand Down Expand Up @@ -66,6 +67,7 @@ export function InboxSignalsTab() {
const suggestedReviewerFilter = useInboxSignalsFilterStore(
(s) => s.suggestedReviewerFilter,
);
const priorityFilter = useInboxSignalsFilterStore((s) => s.priorityFilter);

// ── GitHub integration ───────────────────────────────────────────────
const { hasGithubIntegration } = useRepositoryIntegration();
Expand Down Expand Up @@ -122,13 +124,15 @@ export function InboxSignalsTab() {
suggestedReviewerFilter.length > 0
? buildSuggestedReviewerFilterParam(suggestedReviewerFilter)
: undefined,
priority: buildPriorityFilterParam(priorityFilter),
}),
[
statusFilter,
sortField,
sortDirection,
sourceProductFilter,
suggestedReviewerFilter,
priorityFilter,
],
);

Expand Down Expand Up @@ -386,6 +390,7 @@ export function InboxSignalsTab() {
const hasActiveFilters =
sourceProductFilter.length > 0 ||
suggestedReviewerFilter.length > 0 ||
priorityFilter.length > 0 ||
statusFilter.length < 5;
const shouldShowTwoPane =
hasReports ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import { Box, Flex, Popover, Text } from "@radix-ui/themes";
import type {
SignalReportOrderingField,
SignalReportPriority,
SignalReportStatus,
} from "@shared/types";
import type React from "react";
Expand Down Expand Up @@ -75,6 +76,14 @@ const FILTERABLE_STATUSES: SignalReportStatus[] = [
"potential",
];

const PRIORITY_OPTIONS: { value: SignalReportPriority; accent: string }[] = [
{ value: "P0", accent: "var(--red-9)" },
{ value: "P1", accent: "var(--orange-9)" },
{ value: "P2", accent: "var(--amber-9)" },
{ value: "P3", accent: "var(--gray-9)" },
{ value: "P4", accent: "var(--gray-9)" },
];

const SOURCE_PRODUCT_OPTIONS: {
value: SourceProduct;
label: string;
Expand Down Expand Up @@ -120,6 +129,8 @@ export function FilterSortMenu() {
const toggleSourceProduct = useInboxSignalsFilterStore(
(s) => s.toggleSourceProduct,
);
const priorityFilter = useInboxSignalsFilterStore((s) => s.priorityFilter);
const togglePriority = useInboxSignalsFilterStore((s) => s.togglePriority);

const handleContentKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
Expand Down Expand Up @@ -195,6 +206,35 @@ export function FilterSortMenu() {
</Box>
</Box>

<Box>
<Text className="pl-[1px] font-medium text-[13px] text-gray-10">
Priority
</Text>
<Box mt="1">
{PRIORITY_OPTIONS.map((option) => {
const isActive = priorityFilter.includes(option.value);

return (
<button
key={option.value}
type="button"
className={ITEM_CLASS_NAME}
onClick={() => togglePriority(option.value)}
>
<span className="flex items-center gap-1.5">
<span
className="inline-block h-2 w-2 shrink-0 rounded-full"
style={{ backgroundColor: option.accent }}
/>
<span className="text-gray-12">{option.value}</span>
</span>
{isActive && <Check size={12} className="text-gray-12" />}
</button>
);
})}
</Box>
</Box>

<Box>
<Text className="pl-[1px] font-medium text-[13px] text-gray-10">
Status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe("inboxSignalsFilterStore", () => {
],
sourceProductFilter: [],
suggestedReviewerFilter: [],
priorityFilter: [],
});
});

Expand All @@ -36,6 +37,7 @@ describe("inboxSignalsFilterStore", () => {
]);
expect(state.sourceProductFilter).toEqual([]);
expect(state.suggestedReviewerFilter).toEqual([]);
expect(state.priorityFilter).toEqual([]);
});

it("setSort updates field and direction", () => {
Expand Down Expand Up @@ -106,12 +108,50 @@ describe("inboxSignalsFilterStore", () => {
]);
});

it("togglePriority adds and removes priorities", () => {
useInboxSignalsFilterStore.getState().togglePriority("P0");
expect(useInboxSignalsFilterStore.getState().priorityFilter).toEqual([
"P0",
]);

useInboxSignalsFilterStore.getState().togglePriority("P1");
expect(useInboxSignalsFilterStore.getState().priorityFilter).toEqual([
"P0",
"P1",
]);

useInboxSignalsFilterStore.getState().togglePriority("P0");
expect(useInboxSignalsFilterStore.getState().priorityFilter).toEqual([
"P1",
]);
});

it("setPriorityFilter de-duplicates priorities", () => {
useInboxSignalsFilterStore.getState().setPriorityFilter(["P0", "P1", "P0"]);

expect(useInboxSignalsFilterStore.getState().priorityFilter).toEqual([
"P0",
"P1",
]);
});

it("persists priorityFilter", () => {
useInboxSignalsFilterStore.getState().setPriorityFilter(["P0", "P1"]);

const raw = localStorage.getItem("inbox-signals-filter-storage");
expect(raw).toBeTruthy();
const persisted = JSON.parse(raw as string);

expect(persisted.state.priorityFilter).toEqual(["P0", "P1"]);
});

it("resetFilters restores defaults across all filter fields", () => {
const store = useInboxSignalsFilterStore.getState();
store.setSearchQuery("hello");
store.setStatusFilter(["ready"]);
store.toggleSourceProduct("github");
store.setSuggestedReviewerFilter(["reviewer-1"]);
store.setPriorityFilter(["P0", "P1"]);

useInboxSignalsFilterStore.getState().resetFilters();

Expand All @@ -127,6 +167,7 @@ describe("inboxSignalsFilterStore", () => {
]);
expect(state.sourceProductFilter).toEqual([]);
expect(state.suggestedReviewerFilter).toEqual([]);
expect(state.priorityFilter).toEqual([]);
});

it("resetFilters preserves sort preferences", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {
SignalReportOrderingField,
SignalReportPriority,
SignalReportStatus,
} from "@shared/types";
import { create } from "zustand";
Expand Down Expand Up @@ -39,6 +40,8 @@ interface InboxSignalsFilterState {
sourceProductFilter: SourceProduct[];
/** Empty array means "all suggested reviewers" (no filter). Stored as PostHog user UUID strings. */
suggestedReviewerFilter: string[];
/** Empty array means "all priorities" (no filter). */
priorityFilter: SignalReportPriority[];
}

interface InboxSignalsFilterActions {
Expand All @@ -49,6 +52,8 @@ interface InboxSignalsFilterActions {
toggleSourceProduct: (source: SourceProduct) => void;
toggleSuggestedReviewer: (reviewerUuid: string) => void;
setSuggestedReviewerFilter: (reviewerUuids: string[]) => void;
togglePriority: (priority: SignalReportPriority) => void;
setPriorityFilter: (priorities: SignalReportPriority[]) => void;
/** Reset all filters when a deep link arrives so the linked report isn't hidden. */
resetFilters: () => void;
}
Expand All @@ -65,6 +70,7 @@ export const useInboxSignalsFilterStore = create<InboxSignalsFilterStore>()(
statusFilter: DEFAULT_STATUS_FILTER,
sourceProductFilter: [],
suggestedReviewerFilter: [],
priorityFilter: [],
setSort: (sortField, sortDirection) => set({ sortField, sortDirection }),
setSearchQuery: (searchQuery) => set({ searchQuery }),
setStatusFilter: (statusFilter) => set({ statusFilter }),
Expand Down Expand Up @@ -96,12 +102,25 @@ export const useInboxSignalsFilterStore = create<InboxSignalsFilterStore>()(
set({
suggestedReviewerFilter: Array.from(new Set(reviewerUuids)),
}),
togglePriority: (priority) =>
set((state) => {
const current = state.priorityFilter;
const next = current.includes(priority)
? current.filter((p) => p !== priority)
: [...current, priority];
return { priorityFilter: next };
}),
setPriorityFilter: (priorities) =>
set({
priorityFilter: Array.from(new Set(priorities)),
}),
resetFilters: () =>
set({
searchQuery: "",
statusFilter: DEFAULT_STATUS_FILTER,
sourceProductFilter: [],
suggestedReviewerFilter: [],
priorityFilter: [],
}),
}),
{
Expand All @@ -112,6 +131,7 @@ export const useInboxSignalsFilterStore = create<InboxSignalsFilterStore>()(
statusFilter: state.statusFilter,
sourceProductFilter: state.sourceProductFilter,
suggestedReviewerFilter: state.suggestedReviewerFilter,
priorityFilter: state.priorityFilter,
}),
},
),
Expand Down
17 changes: 17 additions & 0 deletions apps/code/src/renderer/features/inbox/utils/filterReports.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { SignalReport } from "@shared/types";
import { describe, expect, it } from "vitest";
import {
buildPriorityFilterParam,
buildSignalReportListOrdering,
buildSuggestedReviewerFilterParam,
filterReportsBySearch,
Expand Down Expand Up @@ -153,3 +154,19 @@ describe("buildSuggestedReviewerFilterParam", () => {
).toBe("reviewer-1,reviewer-2");
});
});

describe("buildPriorityFilterParam", () => {
it("returns undefined for an empty array", () => {
expect(buildPriorityFilterParam([])).toBeUndefined();
});

it("joins priorities with commas", () => {
expect(buildPriorityFilterParam(["P0", "P1", "P2"])).toBe("P0,P1,P2");
});

it("deduplicates priorities", () => {
expect(buildPriorityFilterParam(["P0", "P1", "P0", "P2", "P1"])).toBe(
"P0,P1,P2",
);
});
});
Comment thread
oliverb123 marked this conversation as resolved.
10 changes: 10 additions & 0 deletions apps/code/src/renderer/features/inbox/utils/filterReports.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {
SignalReport,
SignalReportOrderingField,
SignalReportPriority,
SignalReportStatus,
} from "@shared/types";

Expand Down Expand Up @@ -70,3 +71,12 @@ export function buildSuggestedReviewerFilterParam(

return Array.from(new Set(normalizedIds)).join(",");
}

export function buildPriorityFilterParam(
priorities: SignalReportPriority[],
): string | undefined {
if (priorities.length === 0) {
return undefined;
}
return Array.from(new Set(priorities)).join(",");
}
2 changes: 2 additions & 0 deletions apps/code/src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,8 @@ export interface SignalReportsQueryParams {
source_product?: string;
/** Comma-separated PostHog user UUIDs — only returns reports with these suggested reviewers. */
suggested_reviewers?: string;
/** Comma-separated `P0`–`P4` priorities — only returns reports with one of these priorities. */
priority?: string;
}

/** Values match `SignalReportTask.Relationship` on the PostHog API. */
Expand Down
Loading