Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { SignalReport } from "@shared/types";
import type { SignalReport, SignalReportPriority } 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.each([
{ input: [] as SignalReportPriority[], expected: undefined },
{
input: ["P0", "P1", "P2"] as SignalReportPriority[],
expected: "P0,P1,P2",
},
{
input: ["P0", "P1", "P0", "P2", "P1"] as SignalReportPriority[],
expected: "P0,P1,P2",
},
])("buildPriorityFilterParam($input) → $expected", ({ input, expected }) => {
expect(buildPriorityFilterParam(input)).toBe(expected);
});
});
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