From bdf1ff7dd4985bb65d24508d0da086338055c728 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Fri, 26 Jun 2026 14:21:29 +0100 Subject: [PATCH] Rebuild Not actionable inbox tab on latest master MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Master independently landed the resolved→Archive half of this PR (excluded statuses, isDismissedReport, the resolved status/labels/badge, the Archive query, resolved Restore-hiding in ReportCard/DismissedReportDetail, and the resolved→Archive detail redirect). Re-applying only the still-unique part on top of the moved-on master to clear the merge conflicts: - Add the staff-only Not actionable tab (gated on user.is_staff via INBOX_STAFF_ONLY_TAB_KEYS / isStaffOnlyInboxTab), listing reports the actionability judgment marked not_actionable. - isNotActionableReport mirrors isReportTabReport's gate order (excludes excluded/failed/PR-bearing/in-flight runs) so a report can't double-list in both Runs and Not actionable. - isReportTabReport excludes not-actionable reports so they don't also show in Reports. - Route + detail wiring: /code/inbox/not-actionable list + $reportId detail, ReportCard detailTab, ReportDetail backTo/backLabel, gate + frame + prefetch route unions, regenerated routeTree.gen.ts. - Tests for isNotActionableReport (incl. the in-flight/failed guards) and isStaffOnlyInboxTab; CLAUDE.md updated to a five-tab IA. Generated-By: PostHog Code Task-Id: 3a7dec81-6f1d-425a-a7cb-f468c36b3ec7 --- .../core/src/inbox/reportMembership.test.ts | 85 +++++++++++++++++++ packages/core/src/inbox/reportMembership.ts | 46 +++++++++- packages/ui/src/features/inbox/CLAUDE.md | 14 ++- .../inbox/components/InboxDetailFrame.tsx | 6 +- .../components/InboxReportDetailGate.tsx | 9 ++ .../features/inbox/components/InboxTabBar.tsx | 41 ++++++--- .../inbox/components/NotActionableTab.tsx | 39 +++++++++ .../features/inbox/components/ReportCard.tsx | 18 +++- .../inbox/components/ReportDetail.tsx | 35 ++++++-- .../hooks/useInboxReportDetailPrefetch.ts | 4 + packages/ui/src/router/routeTree.gen.ts | 75 ++++++++++++++++ .../code/inbox/not-actionable.$reportId.tsx | 24 ++++++ .../code/inbox/not-actionable.index.tsx | 6 ++ .../routes/code/inbox/not-actionable.tsx | 5 ++ 14 files changed, 380 insertions(+), 27 deletions(-) create mode 100644 packages/ui/src/features/inbox/components/NotActionableTab.tsx create mode 100644 packages/ui/src/router/routes/code/inbox/not-actionable.$reportId.tsx create mode 100644 packages/ui/src/router/routes/code/inbox/not-actionable.index.tsx create mode 100644 packages/ui/src/router/routes/code/inbox/not-actionable.tsx diff --git a/packages/core/src/inbox/reportMembership.test.ts b/packages/core/src/inbox/reportMembership.test.ts index 62b5715156..5cecdbecc1 100644 --- a/packages/core/src/inbox/reportMembership.test.ts +++ b/packages/core/src/inbox/reportMembership.test.ts @@ -10,8 +10,10 @@ import { isDismissedReport, isExcludedFromInbox, isInboxDetailPath, + isNotActionableReport, isPullRequestReport, isReportTabReport, + isStaffOnlyInboxTab, matchesReviewerScope, partitionRunsTabReports, teammateInboxScope, @@ -58,10 +60,85 @@ describe("isDismissedReport", () => { }); }); +describe("isNotActionableReport", () => { + it("matches reports the judgment marked not_actionable", () => { + expect( + isNotActionableReport(fakeReport({ actionability: "not_actionable" })), + ).toBe(true); + }); + + it.each(["immediately_actionable", "requires_human_input", null] as const)( + "does not match %s actionability", + (actionability) => { + expect(isNotActionableReport(fakeReport({ actionability }))).toBe(false); + }, + ); + + it.each(["suppressed", "resolved"] as const)( + "excludes terminal %s reports", + (status) => { + expect( + isNotActionableReport( + fakeReport({ actionability: "not_actionable", status }), + ), + ).toBe(false); + }, + ); + + it("excludes PR-bearing reports", () => { + expect( + isNotActionableReport( + fakeReport({ + actionability: "not_actionable", + implementation_pr_url: "https://gh/p/1", + }), + ), + ).toBe(false); + }); + + it.each(["potential", "candidate", "in_progress", "pending_input"] as const)( + "excludes in-flight %s runs (they stay in the Runs tab)", + (status) => { + expect( + isNotActionableReport( + fakeReport({ status, actionability: "not_actionable" }), + ), + ).toBe(false); + }, + ); + + it("excludes failed reports (they stay in the Runs tab)", () => { + expect( + isNotActionableReport( + fakeReport({ status: "failed", actionability: "not_actionable" }), + ), + ).toBe(false); + }); + + it("matches a settled (ready) not_actionable report", () => { + expect( + isNotActionableReport( + fakeReport({ status: "ready", actionability: "not_actionable" }), + ), + ).toBe(true); + }); +}); + +describe("isStaffOnlyInboxTab", () => { + it("gates only the Not actionable tab", () => { + expect(isStaffOnlyInboxTab("not-actionable")).toBe(true); + expect(isStaffOnlyInboxTab("reports")).toBe(false); + expect(isStaffOnlyInboxTab("pulls")).toBe(false); + expect(isStaffOnlyInboxTab("runs")).toBe(false); + expect(isStaffOnlyInboxTab("dismissed")).toBe(false); + }); +}); + describe("isInboxDetailPath", () => { it("matches detail paths for each inbox tab", () => { expect(isInboxDetailPath("/code/inbox/pulls/abc")).toBe(true); expect(isInboxDetailPath("/code/inbox/reports/abc")).toBe(true); + expect(isInboxDetailPath("/code/inbox/not-actionable/abc")).toBe(true); expect(isInboxDetailPath("/code/inbox/runs/abc")).toBe(true); }); @@ -236,6 +313,14 @@ describe("tabFilters", () => { ), ).toBe(false); }); + + it("excludes not-actionable reports (they go to the Not actionable tab)", () => { + expect( + isReportTabReport( + fakeReport({ status: "ready", actionability: "not_actionable" }), + ), + ).toBe(false); + }); }); describe("matchesReviewerScope", () => { diff --git a/packages/core/src/inbox/reportMembership.ts b/packages/core/src/inbox/reportMembership.ts index edfca5f9e8..409312345d 100644 --- a/packages/core/src/inbox/reportMembership.ts +++ b/packages/core/src/inbox/reportMembership.ts @@ -82,11 +82,17 @@ export function countInboxScopeReports( return reports.filter((report) => matchesInboxScope(report, scope)).length; } -export type InboxTabKey = "pulls" | "reports" | "runs" | "dismissed"; +export type InboxTabKey = + | "pulls" + | "reports" + | "not-actionable" + | "runs" + | "dismissed"; export const INBOX_TAB_KEYS: InboxTabKey[] = [ "pulls", "reports", + "not-actionable", "runs", "dismissed", ]; @@ -94,10 +100,25 @@ export const INBOX_TAB_KEYS: InboxTabKey[] = [ export const INBOX_TAB_LABEL: Record = { pulls: "Pull requests", reports: "Reports", + "not-actionable": "Not actionable", runs: "Runs", dismissed: "Archive", }; +/** + * Tabs only shown to staff (internal) users. Non-staff see Pull requests, + * Reports, Runs and Archive. The Not actionable tab is an internal signal-quality + * audit surface — reports the agent judged not worth acting on — so it stays + * behind the staff flag, matching the PostHog Cloud inbox. + */ +export const INBOX_STAFF_ONLY_TAB_KEYS = new Set([ + "not-actionable", +]); + +export function isStaffOnlyInboxTab(key: InboxTabKey): boolean { + return INBOX_STAFF_ONLY_TAB_KEYS.has(key); +} + /** * Canonical inbox tab list routes. Use these constants instead of hard-coding * `/code/inbox/pulls` etc., so renames stay in one place. @@ -112,6 +133,7 @@ export const INBOX_TAB_LIST_ROUTE: Record< > = { pulls: "/code/inbox/pulls", reports: "/code/inbox/reports", + "not-actionable": "/code/inbox/not-actionable", runs: "/code/inbox/runs", dismissed: "/code/inbox/dismissed", }; @@ -231,9 +253,31 @@ export function isReportTabReport(report: SignalReport): boolean { // rather than reappearing as a Report. if (report.implementation_pr_url) return false; if (isAgentRunReport(report)) return false; + // Reports the agent judged not worth acting on get their own (staff-only) + // tab and are kept out of the main Reports list, matching the Cloud inbox. + if (isNotActionableReport(report)) return false; return true; } +/** + * Not-actionable tab membership: reports the agentic actionability judgment + * marked `not_actionable`. A staff-only signal-quality audit surface. These are + * still in-pipeline reports (the judgment is an artefact, not a status), so this + * partitions the same main list the other report tabs read — it just keeps them + * out of the Reports tab via the check in `isReportTabReport`. + */ +export function isNotActionableReport(report: SignalReport): boolean { + if (isExcludedFromInbox(report)) return false; + if (report.status === "failed") return false; // failed runs live in the Runs tab only + if (report.implementation_pr_url) return false; + // In-flight (queued/live) runs belong to the Runs tab until they settle, even + // if a not_actionable judgment is already attached. Mirror the gate order in + // `isReportTabReport` so the two predicates classify a report the same way and + // it can't show in both the Runs and Not actionable tabs at once. + if (isAgentRunReport(report)) return false; + return report.actionability === "not_actionable"; +} + export function matchesReviewerScope( report: SignalReport, scope: InboxScope, diff --git a/packages/ui/src/features/inbox/CLAUDE.md b/packages/ui/src/features/inbox/CLAUDE.md index 5d3c33ab37..2f96cb01f4 100644 --- a/packages/ui/src/features/inbox/CLAUDE.md +++ b/packages/ui/src/features/inbox/CLAUDE.md @@ -15,15 +15,24 @@ The main objects are: ## Information Architecture -Inbox has four tabs and one reviewer-scope control: +Inbox has five tabs and one reviewer-scope control: | Tab | Route | Membership | | --- | --- | --- | | Pull requests | `/code/inbox/pulls` | Reports with `implementation_pr_url` set | -| Reports | `/code/inbox/reports` | Reports without a PR and not currently running | +| Reports | `/code/inbox/reports` | Reports without a PR, not running, and not judged not-actionable | +| Not actionable | `/code/inbox/not-actionable` | Reports the judgment marked `actionability === "not_actionable"` (**staff-only**) | | Runs | `/code/inbox/runs` | Reports that are still in progress or waiting on input | | Archive | `/code/inbox/dismissed` | Terminal reports: archived/suppressed (`status === "suppressed"`) and resolved-by-merged-PR (`status === "resolved"`) | +The **Not actionable** tab is gated to staff (internal) users via +`INBOX_STAFF_ONLY_TAB_KEYS` (see `isStaffOnlyInboxTab`); `InboxTabBar` hides it +for non-staff, keyed off `user.is_staff`. It's an internal signal-quality audit +surface and mirrors the same staff-only tab in `PostHog/posthog`. It reuses the +shared `InboxReportListTab` shell (same as Reports/Pulls); cards link to its own +detail route so back-navigation stays on the tab. `isReportTabReport` excludes +not-actionable reports so they don't double-list in Reports. + Detail pages live under the same tab: `/code/inbox//$reportId`. The Archive tab (route `/code/inbox/dismissed`, user-facing label "Archive") is @@ -76,6 +85,7 @@ The tab components are intentionally simple: - `PullRequestsTab` partitions scoped reports with `isPullRequestReport`. - `ReportsTab` partitions with `isReportTabReport`. +- `NotActionableTab` (staff-only) partitions with `isNotActionableReport`. - `RunsTab` partitions with `isAgentRunReport`. - `DismissedTab` (the "Archive" tab) lists its own `useInboxDismissedReports` query (matching `isDismissedReport`); read-only detail route, restore action per card. diff --git a/packages/ui/src/features/inbox/components/InboxDetailFrame.tsx b/packages/ui/src/features/inbox/components/InboxDetailFrame.tsx index 0031823dfc..b265855ece 100644 --- a/packages/ui/src/features/inbox/components/InboxDetailFrame.tsx +++ b/packages/ui/src/features/inbox/components/InboxDetailFrame.tsx @@ -27,7 +27,11 @@ import type { ComponentType, ReactNode } from "react"; interface InboxDetailFrameProps { report: SignalReport; /** List route for the back-link (e.g. "/code/inbox/pulls"). */ - backTo: "/code/inbox/pulls" | "/code/inbox/reports" | "/code/inbox/dismissed"; + backTo: + | "/code/inbox/pulls" + | "/code/inbox/reports" + | "/code/inbox/not-actionable" + | "/code/inbox/dismissed"; backLabel: string; /** * Whether to render the Dismiss button + dialog. Off for already-dismissed diff --git a/packages/ui/src/features/inbox/components/InboxReportDetailGate.tsx b/packages/ui/src/features/inbox/components/InboxReportDetailGate.tsx index 557ba6c150..fc62f3ff4b 100644 --- a/packages/ui/src/features/inbox/components/InboxReportDetailGate.tsx +++ b/packages/ui/src/features/inbox/components/InboxReportDetailGate.tsx @@ -1,5 +1,6 @@ import { isDismissedReport, + isNotActionableReport, isPullRequestReport, isReportTabReport, } from "@posthog/core/inbox/reportMembership"; @@ -21,6 +22,7 @@ interface InboxReportDetailGateProps { backTo: | "/code/inbox/pulls" | "/code/inbox/reports" + | "/code/inbox/not-actionable" | "/code/inbox/runs" | "/code/inbox/dismissed"; backLabel: string; @@ -31,6 +33,7 @@ interface InboxReportDetailGateProps { type InboxDetailRoute = | "/code/inbox/pulls/$reportId" | "/code/inbox/reports/$reportId" + | "/code/inbox/not-actionable/$reportId" | "/code/inbox/runs/$reportId" | "/code/inbox/dismissed/$reportId"; @@ -43,6 +46,8 @@ type InboxDetailRoute = */ function nonSuppressedDetailRoute(report: SignalReport): InboxDetailRoute { if (isPullRequestReport(report)) return "/code/inbox/pulls/$reportId"; + if (isNotActionableReport(report)) + return "/code/inbox/not-actionable/$reportId"; if (isReportTabReport(report)) return "/code/inbox/reports/$reportId"; return "/code/inbox/runs/$reportId"; } @@ -165,7 +170,11 @@ function tabFromBackTo( ): InboxDetailTab | null { if (backTo === "/code/inbox/pulls") return "pulls"; if (backTo === "/code/inbox/runs") return "runs"; + // Archive and the staff-only Not actionable tab aren't part of the triage + // funnel, so their detail opens aren't tracked (rank would be measured against + // the wrong list). if (backTo === "/code/inbox/dismissed") return null; + if (backTo === "/code/inbox/not-actionable") return null; return "reports"; } diff --git a/packages/ui/src/features/inbox/components/InboxTabBar.tsx b/packages/ui/src/features/inbox/components/InboxTabBar.tsx index ef130f3876..61b1881e53 100644 --- a/packages/ui/src/features/inbox/components/InboxTabBar.tsx +++ b/packages/ui/src/features/inbox/components/InboxTabBar.tsx @@ -4,8 +4,10 @@ import { INBOX_TAB_LIST_ROUTE, type InboxTabCounts, type InboxTabKey, + isStaffOnlyInboxTab, } from "@posthog/core/inbox/reportMembership"; import { Tabs, TabsList, TabsTrigger } from "@posthog/quill"; +import { useMeQuery } from "@posthog/ui/features/auth/useMeQuery"; import { InboxScopeSelect } from "@posthog/ui/features/inbox/components/InboxScopeSelect"; import { Flex } from "@radix-ui/themes"; import { useNavigate, useRouterState } from "@tanstack/react-router"; @@ -15,6 +17,11 @@ interface InboxTabBarProps { } function activeTabFromPath(pathname: string): InboxTabKey { + // Check "not-actionable" before "reports": the former is not a prefix of the + // latter, but keeping the more specific routes first guards against future + // overlaps. + if (pathname.startsWith(INBOX_TAB_LIST_ROUTE["not-actionable"])) + return "not-actionable"; if (pathname.startsWith(INBOX_TAB_LIST_ROUTE.reports)) return "reports"; if (pathname.startsWith(INBOX_TAB_LIST_ROUTE.runs)) return "runs"; if (pathname.startsWith(INBOX_TAB_LIST_ROUTE.dismissed)) return "dismissed"; @@ -25,6 +32,11 @@ export function InboxTabBar({ counts }: InboxTabBarProps) { const navigate = useNavigate(); const pathname = useRouterState({ select: (s) => s.location.pathname }); const activeKey = activeTabFromPath(pathname); + const { data: currentUser } = useMeQuery(); + const isStaff = currentUser?.is_staff === true; + const visibleTabKeys = INBOX_TAB_KEYS.filter( + (key) => isStaff || !isStaffOnlyInboxTab(key), + ); return ( @@ -39,7 +51,7 @@ export function InboxTabBar({ counts }: InboxTabBarProps) { variant="line" className="h-auto gap-0.5 [&_.quill-tabs__indicator]:transition-[transform,width]! [&_.quill-tabs__indicator]:duration-100! [&_.quill-tabs__indicator]:ease-out!" > - {INBOX_TAB_KEYS.map((key) => { + {visibleTabKeys.map((key) => { const isActive = key === activeKey; return ( {INBOX_TAB_LABEL[key]} - {/* Runs and the open-ended Archive don't get a running total — it adds no signal. */} - {key !== "runs" && key !== "dismissed" && counts[key] > 0 && ( - - {counts[key]} - - )} + {/* Runs, Not actionable, and the open-ended Archive don't get a running total — it adds no signal. */} + {key !== "runs" && + key !== "dismissed" && + key !== "not-actionable" && + counts[key] > 0 && ( + + {counts[key]} + + )} ); })} diff --git a/packages/ui/src/features/inbox/components/NotActionableTab.tsx b/packages/ui/src/features/inbox/components/NotActionableTab.tsx new file mode 100644 index 0000000000..8e31990ed9 --- /dev/null +++ b/packages/ui/src/features/inbox/components/NotActionableTab.tsx @@ -0,0 +1,39 @@ +import { InfoIcon } from "@phosphor-icons/react"; +import { isNotActionableReport } from "@posthog/core/inbox/reportMembership"; +import { InboxReportListTab } from "@posthog/ui/features/inbox/components/InboxReportListTab"; +import { + ReportCard, + type ReportCardProps, +} from "@posthog/ui/features/inbox/components/ReportCard"; + +// Link cards (and their back navigation) at the Not actionable tab rather than +// the Reports tab. +function NotActionableReportCard( + props: Extract, +) { + return ; +} + +/** + * Staff-only (internal) tab listing reports the agentic actionability judgment + * marked `not_actionable`. Same list shell as Pull requests / Reports — only the + * predicate differs — so the team can audit signal quality. These reports are + * kept out of the Reports tab (see `isReportTabReport`). + */ +export function NotActionableTab() { + return ( + + ); +} diff --git a/packages/ui/src/features/inbox/components/ReportCard.tsx b/packages/ui/src/features/inbox/components/ReportCard.tsx index a58955c4af..1023395feb 100644 --- a/packages/ui/src/features/inbox/components/ReportCard.tsx +++ b/packages/ui/src/features/inbox/components/ReportCard.tsx @@ -40,6 +40,10 @@ interface DefaultReportCardProps extends BaseReportCardProps { onDismiss: () => void; dismissDisabledReason?: string | null; isDismissPending?: boolean; + // Which tab the card's detail link (and back navigation) should target. + // Defaults to the Reports tab; the Not actionable tab passes its own key so + // its detail view returns there instead of to Reports. + detailTab?: "reports" | "not-actionable"; } /** @@ -63,15 +67,21 @@ export function ReportCard(props: ReportCardProps) { // Archive tab for reference, badged as resolved, with no restore action. const isResolved = report.status === "resolved"; + const detailTab = props.variant === "archived" ? "reports" : props.detailTab; const detailRoute = isArchived ? { to: "/code/inbox/dismissed/$reportId" as const, params: { reportId: report.id }, } - : { - to: "/code/inbox/reports/$reportId" as const, - params: { reportId: report.id }, - }; + : detailTab === "not-actionable" + ? { + to: "/code/inbox/not-actionable/$reportId" as const, + params: { reportId: report.id }, + } + : { + to: "/code/inbox/reports/$reportId" as const, + params: { reportId: report.id }, + }; const { prefetch, pointerHandlers } = useInboxReportDetailPrefetch( report, detailRoute, diff --git a/packages/ui/src/features/inbox/components/ReportDetail.tsx b/packages/ui/src/features/inbox/components/ReportDetail.tsx index ea1492145c..4c841e1b91 100644 --- a/packages/ui/src/features/inbox/components/ReportDetail.tsx +++ b/packages/ui/src/features/inbox/components/ReportDetail.tsx @@ -13,34 +13,57 @@ import { ReportTasksSection } from "@posthog/ui/features/inbox/components/Report import { SuggestedReviewersSection } from "@posthog/ui/features/inbox/components/SuggestedReviewersSection"; import { copyInboxReportLink } from "@posthog/ui/features/inbox/utils/copyInboxReportLink"; +/** Tabs whose detail view renders a `ReportDetail`. */ +type ReportDetailBackTo = "/code/inbox/reports" | "/code/inbox/not-actionable"; + interface ReportDetailProps { reportId: string; cachedReport?: SignalReport | null; + /** Where the back link points. Defaults to the Reports tab. */ + backTo?: ReportDetailBackTo; + /** Label for the back link. Defaults to "Back to reports". */ + backLabel?: string; } export function ReportDetail({ reportId, cachedReport = null, + backTo = "/code/inbox/reports", + backLabel = "Back to reports", }: ReportDetailProps) { return ( - {(report) => } + {(report) => ( + + )} ); } -function ReportDetailContent({ report }: { report: SignalReport }) { +function ReportDetailContent({ + report, + backTo, + backLabel, +}: { + report: SignalReport; + backTo: ReportDetailBackTo; + backLabel: string; +}) { return ( diff --git a/packages/ui/src/features/inbox/hooks/useInboxReportDetailPrefetch.ts b/packages/ui/src/features/inbox/hooks/useInboxReportDetailPrefetch.ts index a88b2def30..55779cc5dd 100644 --- a/packages/ui/src/features/inbox/hooks/useInboxReportDetailPrefetch.ts +++ b/packages/ui/src/features/inbox/hooks/useInboxReportDetailPrefetch.ts @@ -13,6 +13,10 @@ export type InboxDetailRoute = to: "/code/inbox/reports/$reportId"; params: { reportId: string }; } + | { + to: "/code/inbox/not-actionable/$reportId"; + params: { reportId: string }; + } | { to: "/code/inbox/runs/$reportId"; params: { reportId: string }; diff --git a/packages/ui/src/router/routeTree.gen.ts b/packages/ui/src/router/routeTree.gen.ts index f830809c2e..6489c106de 100644 --- a/packages/ui/src/router/routeTree.gen.ts +++ b/packages/ui/src/router/routeTree.gen.ts @@ -37,6 +37,7 @@ import { Route as CodeTasksTaskIdRouteImport } from './routes/code/tasks/$taskId import { Route as CodeInboxRunsRouteImport } from './routes/code/inbox/runs' import { Route as CodeInboxReportsRouteImport } from './routes/code/inbox/reports' import { Route as CodeInboxPullsRouteImport } from './routes/code/inbox/pulls' +import { Route as CodeInboxNotActionableRouteImport } from './routes/code/inbox/not-actionable' import { Route as CodeInboxDismissedRouteImport } from './routes/code/inbox/dismissed' import { Route as CodeInboxAgentsRouteImport } from './routes/code/inbox/agents' import { Route as CodeAgentsScoutsRouteImport } from './routes/code/agents/scouts' @@ -44,6 +45,7 @@ import { Route as CodeAgentsApplicationsRouteImport } from './routes/code/agents import { Route as CodeInboxRunsIndexRouteImport } from './routes/code/inbox/runs.index' import { Route as CodeInboxReportsIndexRouteImport } from './routes/code/inbox/reports.index' import { Route as CodeInboxPullsIndexRouteImport } from './routes/code/inbox/pulls.index' +import { Route as CodeInboxNotActionableIndexRouteImport } from './routes/code/inbox/not-actionable.index' import { Route as CodeInboxDismissedIndexRouteImport } from './routes/code/inbox/dismissed.index' import { Route as CodeAgentsScoutsIndexRouteImport } from './routes/code/agents/scouts.index' import { Route as CodeAgentsApplicationsIndexRouteImport } from './routes/code/agents/applications/index' @@ -53,6 +55,7 @@ import { Route as CodeTasksPendingKeyRouteImport } from './routes/code/tasks/pen import { Route as CodeInboxRunsReportIdRouteImport } from './routes/code/inbox/runs.$reportId' import { Route as CodeInboxReportsReportIdRouteImport } from './routes/code/inbox/reports.$reportId' import { Route as CodeInboxPullsReportIdRouteImport } from './routes/code/inbox/pulls.$reportId' +import { Route as CodeInboxNotActionableReportIdRouteImport } from './routes/code/inbox/not-actionable.$reportId' import { Route as CodeInboxDismissedReportIdRouteImport } from './routes/code/inbox/dismissed.$reportId' import { Route as CodeAgentsScoutsSkillNameRouteImport } from './routes/code/agents/scouts.$skillName' import { Route as CodeAgentsApplicationsApprovalsRouteImport } from './routes/code/agents/applications/approvals' @@ -208,6 +211,11 @@ const CodeInboxPullsRoute = CodeInboxPullsRouteImport.update({ path: '/pulls', getParentRoute: () => CodeInboxRoute, } as any) +const CodeInboxNotActionableRoute = CodeInboxNotActionableRouteImport.update({ + id: '/not-actionable', + path: '/not-actionable', + getParentRoute: () => CodeInboxRoute, +} as any) const CodeInboxDismissedRoute = CodeInboxDismissedRouteImport.update({ id: '/dismissed', path: '/dismissed', @@ -243,6 +251,12 @@ const CodeInboxPullsIndexRoute = CodeInboxPullsIndexRouteImport.update({ path: '/', getParentRoute: () => CodeInboxPullsRoute, } as any) +const CodeInboxNotActionableIndexRoute = + CodeInboxNotActionableIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => CodeInboxNotActionableRoute, + } as any) const CodeInboxDismissedIndexRoute = CodeInboxDismissedIndexRouteImport.update({ id: '/', path: '/', @@ -292,6 +306,12 @@ const CodeInboxPullsReportIdRoute = CodeInboxPullsReportIdRouteImport.update({ path: '/$reportId', getParentRoute: () => CodeInboxPullsRoute, } as any) +const CodeInboxNotActionableReportIdRoute = + CodeInboxNotActionableReportIdRouteImport.update({ + id: '/$reportId', + path: '/$reportId', + getParentRoute: () => CodeInboxNotActionableRoute, + } as any) const CodeInboxDismissedReportIdRoute = CodeInboxDismissedReportIdRouteImport.update({ id: '/$reportId', @@ -401,6 +421,7 @@ export interface FileRoutesByFullPath { '/code/agents/scouts': typeof CodeAgentsScoutsRouteWithChildren '/code/inbox/agents': typeof CodeInboxAgentsRoute '/code/inbox/dismissed': typeof CodeInboxDismissedRouteWithChildren + '/code/inbox/not-actionable': typeof CodeInboxNotActionableRouteWithChildren '/code/inbox/pulls': typeof CodeInboxPullsRouteWithChildren '/code/inbox/reports': typeof CodeInboxReportsRouteWithChildren '/code/inbox/runs': typeof CodeInboxRunsRouteWithChildren @@ -414,6 +435,7 @@ export interface FileRoutesByFullPath { '/code/agents/applications/approvals': typeof CodeAgentsApplicationsApprovalsRoute '/code/agents/scouts/$skillName': typeof CodeAgentsScoutsSkillNameRouteWithChildren '/code/inbox/dismissed/$reportId': typeof CodeInboxDismissedReportIdRoute + '/code/inbox/not-actionable/$reportId': typeof CodeInboxNotActionableReportIdRoute '/code/inbox/pulls/$reportId': typeof CodeInboxPullsReportIdRoute '/code/inbox/reports/$reportId': typeof CodeInboxReportsReportIdRoute '/code/inbox/runs/$reportId': typeof CodeInboxRunsReportIdRoute @@ -423,6 +445,7 @@ export interface FileRoutesByFullPath { '/code/agents/applications/': typeof CodeAgentsApplicationsIndexRoute '/code/agents/scouts/': typeof CodeAgentsScoutsIndexRoute '/code/inbox/dismissed/': typeof CodeInboxDismissedIndexRoute + '/code/inbox/not-actionable/': typeof CodeInboxNotActionableIndexRoute '/code/inbox/pulls/': typeof CodeInboxPullsIndexRoute '/code/inbox/reports/': typeof CodeInboxReportsIndexRoute '/code/inbox/runs/': typeof CodeInboxRunsIndexRoute @@ -463,6 +486,7 @@ export interface FileRoutesByTo { '/website/$channelId': typeof WebsiteChannelIdIndexRoute '/code/agents/applications/approvals': typeof CodeAgentsApplicationsApprovalsRoute '/code/inbox/dismissed/$reportId': typeof CodeInboxDismissedReportIdRoute + '/code/inbox/not-actionable/$reportId': typeof CodeInboxNotActionableReportIdRoute '/code/inbox/pulls/$reportId': typeof CodeInboxPullsReportIdRoute '/code/inbox/reports/$reportId': typeof CodeInboxReportsReportIdRoute '/code/inbox/runs/$reportId': typeof CodeInboxRunsReportIdRoute @@ -472,6 +496,7 @@ export interface FileRoutesByTo { '/code/agents/applications': typeof CodeAgentsApplicationsIndexRoute '/code/agents/scouts': typeof CodeAgentsScoutsIndexRoute '/code/inbox/dismissed': typeof CodeInboxDismissedIndexRoute + '/code/inbox/not-actionable': typeof CodeInboxNotActionableIndexRoute '/code/inbox/pulls': typeof CodeInboxPullsIndexRoute '/code/inbox/reports': typeof CodeInboxReportsIndexRoute '/code/inbox/runs': typeof CodeInboxRunsIndexRoute @@ -511,6 +536,7 @@ export interface FileRoutesById { '/code/agents/scouts': typeof CodeAgentsScoutsRouteWithChildren '/code/inbox/agents': typeof CodeInboxAgentsRoute '/code/inbox/dismissed': typeof CodeInboxDismissedRouteWithChildren + '/code/inbox/not-actionable': typeof CodeInboxNotActionableRouteWithChildren '/code/inbox/pulls': typeof CodeInboxPullsRouteWithChildren '/code/inbox/reports': typeof CodeInboxReportsRouteWithChildren '/code/inbox/runs': typeof CodeInboxRunsRouteWithChildren @@ -524,6 +550,7 @@ export interface FileRoutesById { '/code/agents/applications/approvals': typeof CodeAgentsApplicationsApprovalsRoute '/code/agents/scouts/$skillName': typeof CodeAgentsScoutsSkillNameRouteWithChildren '/code/inbox/dismissed/$reportId': typeof CodeInboxDismissedReportIdRoute + '/code/inbox/not-actionable/$reportId': typeof CodeInboxNotActionableReportIdRoute '/code/inbox/pulls/$reportId': typeof CodeInboxPullsReportIdRoute '/code/inbox/reports/$reportId': typeof CodeInboxReportsReportIdRoute '/code/inbox/runs/$reportId': typeof CodeInboxRunsReportIdRoute @@ -533,6 +560,7 @@ export interface FileRoutesById { '/code/agents/applications/': typeof CodeAgentsApplicationsIndexRoute '/code/agents/scouts/': typeof CodeAgentsScoutsIndexRoute '/code/inbox/dismissed/': typeof CodeInboxDismissedIndexRoute + '/code/inbox/not-actionable/': typeof CodeInboxNotActionableIndexRoute '/code/inbox/pulls/': typeof CodeInboxPullsIndexRoute '/code/inbox/reports/': typeof CodeInboxReportsIndexRoute '/code/inbox/runs/': typeof CodeInboxRunsIndexRoute @@ -573,6 +601,7 @@ export interface FileRouteTypes { | '/code/agents/scouts' | '/code/inbox/agents' | '/code/inbox/dismissed' + | '/code/inbox/not-actionable' | '/code/inbox/pulls' | '/code/inbox/reports' | '/code/inbox/runs' @@ -586,6 +615,7 @@ export interface FileRouteTypes { | '/code/agents/applications/approvals' | '/code/agents/scouts/$skillName' | '/code/inbox/dismissed/$reportId' + | '/code/inbox/not-actionable/$reportId' | '/code/inbox/pulls/$reportId' | '/code/inbox/reports/$reportId' | '/code/inbox/runs/$reportId' @@ -595,6 +625,7 @@ export interface FileRouteTypes { | '/code/agents/applications/' | '/code/agents/scouts/' | '/code/inbox/dismissed/' + | '/code/inbox/not-actionable/' | '/code/inbox/pulls/' | '/code/inbox/reports/' | '/code/inbox/runs/' @@ -635,6 +666,7 @@ export interface FileRouteTypes { | '/website/$channelId' | '/code/agents/applications/approvals' | '/code/inbox/dismissed/$reportId' + | '/code/inbox/not-actionable/$reportId' | '/code/inbox/pulls/$reportId' | '/code/inbox/reports/$reportId' | '/code/inbox/runs/$reportId' @@ -644,6 +676,7 @@ export interface FileRouteTypes { | '/code/agents/applications' | '/code/agents/scouts' | '/code/inbox/dismissed' + | '/code/inbox/not-actionable' | '/code/inbox/pulls' | '/code/inbox/reports' | '/code/inbox/runs' @@ -682,6 +715,7 @@ export interface FileRouteTypes { | '/code/agents/scouts' | '/code/inbox/agents' | '/code/inbox/dismissed' + | '/code/inbox/not-actionable' | '/code/inbox/pulls' | '/code/inbox/reports' | '/code/inbox/runs' @@ -695,6 +729,7 @@ export interface FileRouteTypes { | '/code/agents/applications/approvals' | '/code/agents/scouts/$skillName' | '/code/inbox/dismissed/$reportId' + | '/code/inbox/not-actionable/$reportId' | '/code/inbox/pulls/$reportId' | '/code/inbox/reports/$reportId' | '/code/inbox/runs/$reportId' @@ -704,6 +739,7 @@ export interface FileRouteTypes { | '/code/agents/applications/' | '/code/agents/scouts/' | '/code/inbox/dismissed/' + | '/code/inbox/not-actionable/' | '/code/inbox/pulls/' | '/code/inbox/reports/' | '/code/inbox/runs/' @@ -935,6 +971,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof CodeInboxPullsRouteImport parentRoute: typeof CodeInboxRoute } + '/code/inbox/not-actionable': { + id: '/code/inbox/not-actionable' + path: '/not-actionable' + fullPath: '/code/inbox/not-actionable' + preLoaderRoute: typeof CodeInboxNotActionableRouteImport + parentRoute: typeof CodeInboxRoute + } '/code/inbox/dismissed': { id: '/code/inbox/dismissed' path: '/dismissed' @@ -984,6 +1027,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof CodeInboxPullsIndexRouteImport parentRoute: typeof CodeInboxPullsRoute } + '/code/inbox/not-actionable/': { + id: '/code/inbox/not-actionable/' + path: '/' + fullPath: '/code/inbox/not-actionable/' + preLoaderRoute: typeof CodeInboxNotActionableIndexRouteImport + parentRoute: typeof CodeInboxNotActionableRoute + } '/code/inbox/dismissed/': { id: '/code/inbox/dismissed/' path: '/' @@ -1047,6 +1097,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof CodeInboxPullsReportIdRouteImport parentRoute: typeof CodeInboxPullsRoute } + '/code/inbox/not-actionable/$reportId': { + id: '/code/inbox/not-actionable/$reportId' + path: '/$reportId' + fullPath: '/code/inbox/not-actionable/$reportId' + preLoaderRoute: typeof CodeInboxNotActionableReportIdRouteImport + parentRoute: typeof CodeInboxNotActionableRoute + } '/code/inbox/dismissed/$reportId': { id: '/code/inbox/dismissed/$reportId' path: '/$reportId' @@ -1294,6 +1351,22 @@ const CodeInboxDismissedRouteChildren: CodeInboxDismissedRouteChildren = { const CodeInboxDismissedRouteWithChildren = CodeInboxDismissedRoute._addFileChildren(CodeInboxDismissedRouteChildren) +interface CodeInboxNotActionableRouteChildren { + CodeInboxNotActionableReportIdRoute: typeof CodeInboxNotActionableReportIdRoute + CodeInboxNotActionableIndexRoute: typeof CodeInboxNotActionableIndexRoute +} + +const CodeInboxNotActionableRouteChildren: CodeInboxNotActionableRouteChildren = + { + CodeInboxNotActionableReportIdRoute: CodeInboxNotActionableReportIdRoute, + CodeInboxNotActionableIndexRoute: CodeInboxNotActionableIndexRoute, + } + +const CodeInboxNotActionableRouteWithChildren = + CodeInboxNotActionableRoute._addFileChildren( + CodeInboxNotActionableRouteChildren, + ) + interface CodeInboxPullsRouteChildren { CodeInboxPullsReportIdRoute: typeof CodeInboxPullsReportIdRoute CodeInboxPullsIndexRoute: typeof CodeInboxPullsIndexRoute @@ -1338,6 +1411,7 @@ const CodeInboxRunsRouteWithChildren = CodeInboxRunsRoute._addFileChildren( interface CodeInboxRouteChildren { CodeInboxAgentsRoute: typeof CodeInboxAgentsRoute CodeInboxDismissedRoute: typeof CodeInboxDismissedRouteWithChildren + CodeInboxNotActionableRoute: typeof CodeInboxNotActionableRouteWithChildren CodeInboxPullsRoute: typeof CodeInboxPullsRouteWithChildren CodeInboxReportsRoute: typeof CodeInboxReportsRouteWithChildren CodeInboxRunsRoute: typeof CodeInboxRunsRouteWithChildren @@ -1347,6 +1421,7 @@ interface CodeInboxRouteChildren { const CodeInboxRouteChildren: CodeInboxRouteChildren = { CodeInboxAgentsRoute: CodeInboxAgentsRoute, CodeInboxDismissedRoute: CodeInboxDismissedRouteWithChildren, + CodeInboxNotActionableRoute: CodeInboxNotActionableRouteWithChildren, CodeInboxPullsRoute: CodeInboxPullsRouteWithChildren, CodeInboxReportsRoute: CodeInboxReportsRouteWithChildren, CodeInboxRunsRoute: CodeInboxRunsRouteWithChildren, diff --git a/packages/ui/src/router/routes/code/inbox/not-actionable.$reportId.tsx b/packages/ui/src/router/routes/code/inbox/not-actionable.$reportId.tsx new file mode 100644 index 0000000000..9e01feb0fb --- /dev/null +++ b/packages/ui/src/router/routes/code/inbox/not-actionable.$reportId.tsx @@ -0,0 +1,24 @@ +import type { SignalReport } from "@posthog/shared/types"; +import { ReportDetail } from "@posthog/ui/features/inbox/components/ReportDetail"; +import { getCachedInboxReportDetail } from "@posthog/ui/features/inbox/inboxQueries"; +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/code/inbox/not-actionable/$reportId")({ + component: NotActionableReportDetailRoute, + pendingComponent: () => null, + loader: ({ params }): SignalReport | null => + getCachedInboxReportDetail(params.reportId) ?? null, +}); + +function NotActionableReportDetailRoute() { + const { reportId } = Route.useParams(); + const cachedReport = Route.useLoaderData(); + return ( + + ); +} diff --git a/packages/ui/src/router/routes/code/inbox/not-actionable.index.tsx b/packages/ui/src/router/routes/code/inbox/not-actionable.index.tsx new file mode 100644 index 0000000000..c0d3630bcc --- /dev/null +++ b/packages/ui/src/router/routes/code/inbox/not-actionable.index.tsx @@ -0,0 +1,6 @@ +import { NotActionableTab } from "@posthog/ui/features/inbox/components/NotActionableTab"; +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/code/inbox/not-actionable/")({ + component: NotActionableTab, +}); diff --git a/packages/ui/src/router/routes/code/inbox/not-actionable.tsx b/packages/ui/src/router/routes/code/inbox/not-actionable.tsx new file mode 100644 index 0000000000..69c40f50d1 --- /dev/null +++ b/packages/ui/src/router/routes/code/inbox/not-actionable.tsx @@ -0,0 +1,5 @@ +import { createFileRoute, Outlet } from "@tanstack/react-router"; + +export const Route = createFileRoute("/code/inbox/not-actionable")({ + component: Outlet, +});