diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index a39d37418..a937fb8cb 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -1,3 +1,5 @@ +import { useOptionalAuthenticatedClient } from "@features/auth/hooks/authClient"; +import { useCurrentUser } from "@features/auth/hooks/authQueries"; import { SelectReportPane, SkeletonBackdrop, @@ -66,6 +68,21 @@ export function InboxSignalsTab() { const suggestedReviewerFilter = useInboxSignalsFilterStore( (s) => s.suggestedReviewerFilter, ); + const seedSuggestedReviewerFilterWithCurrentUser = useInboxSignalsFilterStore( + (s) => s.seedSuggestedReviewerFilterWithCurrentUser, + ); + + // ── Current user (seeds reviewer filter on first inbox visit) ─────────── + const authClient = useOptionalAuthenticatedClient(); + const { data: currentUser } = useCurrentUser({ + client: authClient, + enabled: !!authClient, + }); + + useEffect(() => { + if (!currentUser?.uuid) return; + seedSuggestedReviewerFilterWithCurrentUser(currentUser.uuid); + }, [currentUser?.uuid, seedSuggestedReviewerFilterWithCurrentUser]); // ── GitHub integration ─────────────────────────────────────────────── const { hasGithubIntegration } = useRepositoryIntegration(); @@ -283,18 +300,12 @@ export function InboxSignalsTab() { ); } else if (event.metaKey) { toggleReportSelection(reportId); - } else if ( - selectedReportIdsRef.current.length === 1 && - selectedReportIdsRef.current[0] === reportId - ) { - // Plain click on the only selected report — deselect it - clearSelection(); } else { - // Plain click — select only this report + // Plain click — select only this report (no-op if already the sole selection) setSelectedReportIds([reportId]); } }, - [selectRange, toggleReportSelection, setSelectedReportIds, clearSelection], + [selectRange, toggleReportSelection, setSelectedReportIds], ); // Select-all checkbox diff --git a/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx b/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx index 4dd14652d..c8680a9ca 100644 --- a/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx +++ b/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx @@ -17,6 +17,7 @@ import { CaretRightIcon, EyeIcon, LinkSimpleIcon, + Plus, ThumbsDownIcon, WarningIcon, XIcon, @@ -298,12 +299,25 @@ export function ReportDetailPane({ - {headerImplementationPrUrl ? ( - - ) : null} + + + - + + Create PR + + ) : null} - )} + + {taskStartedAt ? ( + + + {bar.prUrl + ? summary.label + : relationship === "implementation" && + (task?.latest_run?.status === "queued" || + task?.latest_run?.status === "in_progress") + ? "Working on a PR…" + : summary.label} + + + ) : bar.prUrl ? ( + summary.label + ) : relationship === "implementation" && + (task?.latest_run?.status === "queued" || + task?.latest_run?.status === "in_progress") ? ( + "Working on a PR…" + ) : ( + summary.label + )} + {isInteractive && ( can't contain the nested run-action - ) + ) : (
- {verified ? ( - - ) : ( - - )} - {verified ? "Verified" : "Unverified"} + + Verified ); } @@ -266,15 +259,7 @@ function SignalCardHeader({ {signalCardSourceLine(signal)} - {verified !== undefined && } - - Weight: {signal.weight.toFixed(1)} - + {verified === true && } ); } @@ -669,10 +654,34 @@ function ErrorTrackingSignalCard({ codePaths?: string[]; dataQueried?: string; }) { + const projectId = useAuthStateValue((s) => s.projectId); + const cloudRegion = useAuthStateValue((s) => s.cloudRegion); + const issueUrl = signal.source_id + ? errorTrackingIssueUrl(signal.source_id, { projectId, cloudRegion }) + : null; + return ( + {issueUrl && ( + + + View issue + + + + )} diff --git a/apps/code/src/renderer/features/inbox/components/list/ReportListRow.tsx b/apps/code/src/renderer/features/inbox/components/list/ReportListRow.tsx index 003406683..21a046282 100644 --- a/apps/code/src/renderer/features/inbox/components/list/ReportListRow.tsx +++ b/apps/code/src/renderer/features/inbox/components/list/ReportListRow.tsx @@ -75,18 +75,10 @@ export function ReportListRow({ onClick({ metaKey: e.metaKey, shiftKey: e.shiftKey }); }; - const rowBgClass = isSelected - ? report.is_suggested_reviewer - ? "bg-amber-3" - : "bg-gray-3" - : report.is_suggested_reviewer - ? "bg-amber-2" - : ""; + const rowBgClass = isSelected ? "bg-gray-3" : ""; const hoverOverlayClass = - isSelected && report.is_suggested_reviewer - ? "before:bg-amber-12 before:opacity-0 hover:before:opacity-[0.07]" - : "before:bg-gray-12 before:opacity-0 hover:before:opacity-[0.07]"; + "before:bg-gray-12 before:opacity-0 hover:before:opacity-[0.07]"; return ( diff --git a/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.test.ts b/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.test.ts index 00693aa44..ff698fa17 100644 --- a/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.test.ts +++ b/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.test.ts @@ -18,6 +18,7 @@ describe("inboxSignalsFilterStore", () => { ], sourceProductFilter: [], suggestedReviewerFilter: [], + hasInitializedSuggestedReviewerFilter: false, }); }); @@ -129,6 +130,45 @@ describe("inboxSignalsFilterStore", () => { expect(state.suggestedReviewerFilter).toEqual([]); }); + it("seedSuggestedReviewerFilterWithCurrentUser seeds when empty and uninitialized", () => { + useInboxSignalsFilterStore + .getState() + .seedSuggestedReviewerFilterWithCurrentUser("me-uuid"); + + const state = useInboxSignalsFilterStore.getState(); + expect(state.suggestedReviewerFilter).toEqual(["me-uuid"]); + expect(state.hasInitializedSuggestedReviewerFilter).toBe(true); + }); + + it("seedSuggestedReviewerFilterWithCurrentUser is a no-op once initialized", () => { + useInboxSignalsFilterStore + .getState() + .seedSuggestedReviewerFilterWithCurrentUser("me-uuid"); + useInboxSignalsFilterStore.getState().setSuggestedReviewerFilter([]); + + useInboxSignalsFilterStore + .getState() + .seedSuggestedReviewerFilterWithCurrentUser("me-uuid"); + + expect( + useInboxSignalsFilterStore.getState().suggestedReviewerFilter, + ).toEqual([]); + }); + + it("seedSuggestedReviewerFilterWithCurrentUser preserves an existing non-empty filter", () => { + useInboxSignalsFilterStore + .getState() + .setSuggestedReviewerFilter(["someone-else"]); + + useInboxSignalsFilterStore + .getState() + .seedSuggestedReviewerFilterWithCurrentUser("me-uuid"); + + const state = useInboxSignalsFilterStore.getState(); + expect(state.suggestedReviewerFilter).toEqual(["someone-else"]); + expect(state.hasInitializedSuggestedReviewerFilter).toBe(true); + }); + it("resetFilters preserves sort preferences", () => { useInboxSignalsFilterStore.getState().setSort("created_at", "asc"); diff --git a/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts b/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts index c53c4c9e5..31da51321 100644 --- a/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts +++ b/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts @@ -39,6 +39,8 @@ interface InboxSignalsFilterState { sourceProductFilter: SourceProduct[]; /** Empty array means "all suggested reviewers" (no filter). Stored as PostHog user UUID strings. */ suggestedReviewerFilter: string[]; + /** Tracks whether we've seeded the reviewer filter with the current user once. Persisted so the seed only runs on first inbox visit. */ + hasInitializedSuggestedReviewerFilter: boolean; } interface InboxSignalsFilterActions { @@ -49,6 +51,12 @@ interface InboxSignalsFilterActions { toggleSourceProduct: (source: SourceProduct) => void; toggleSuggestedReviewer: (reviewerUuid: string) => void; setSuggestedReviewerFilter: (reviewerUuids: string[]) => void; + /** + * Seed the reviewer filter with the current user on first inbox visit. + * No-op if already initialized, or if the user has actively chosen reviewers. + * Always flips the initialized flag so we don't override later user choices. + */ + seedSuggestedReviewerFilterWithCurrentUser: (currentUserUuid: string) => void; /** Reset all filters when a deep link arrives so the linked report isn't hidden. */ resetFilters: () => void; } @@ -65,6 +73,7 @@ export const useInboxSignalsFilterStore = create()( statusFilter: DEFAULT_STATUS_FILTER, sourceProductFilter: [], suggestedReviewerFilter: [], + hasInitializedSuggestedReviewerFilter: false, setSort: (sortField, sortDirection) => set({ sortField, sortDirection }), setSearchQuery: (searchQuery) => set({ searchQuery }), setStatusFilter: (statusFilter) => set({ statusFilter }), @@ -96,6 +105,17 @@ export const useInboxSignalsFilterStore = create()( set({ suggestedReviewerFilter: Array.from(new Set(reviewerUuids)), }), + seedSuggestedReviewerFilterWithCurrentUser: (currentUserUuid) => + set((state) => { + if (state.hasInitializedSuggestedReviewerFilter) return {}; + return { + hasInitializedSuggestedReviewerFilter: true, + suggestedReviewerFilter: + state.suggestedReviewerFilter.length === 0 + ? [currentUserUuid] + : state.suggestedReviewerFilter, + }; + }), resetFilters: () => set({ searchQuery: "", @@ -112,6 +132,8 @@ export const useInboxSignalsFilterStore = create()( statusFilter: state.statusFilter, sourceProductFilter: state.sourceProductFilter, suggestedReviewerFilter: state.suggestedReviewerFilter, + hasInitializedSuggestedReviewerFilter: + state.hasInitializedSuggestedReviewerFilter, }), }, ), diff --git a/apps/code/src/renderer/utils/posthogLinks.ts b/apps/code/src/renderer/utils/posthogLinks.ts index 8a74b5120..5512b0ea4 100644 --- a/apps/code/src/renderer/utils/posthogLinks.ts +++ b/apps/code/src/renderer/utils/posthogLinks.ts @@ -65,3 +65,13 @@ export function experimentUrl( export function featureFlagsIndexUrl(overrides?: LinkOverrides): string | null { return withProjectId((pid) => `/project/${pid}/feature_flags`, overrides); } + +export function errorTrackingIssueUrl( + issueId: string, + overrides?: LinkOverrides, +): string | null { + return withProjectId( + (pid) => `/project/${pid}/error_tracking/${encodeURIComponent(issueId)}`, + overrides, + ); +}