From b2afe170f0239ac9caf118edbc47174643087769 Mon Sep 17 00:00:00 2001 From: Rafael Audibert <32079912+rafaeelaudibert@users.noreply.github.com> Date: Tue, 16 Jun 2026 01:04:17 -0300 Subject: [PATCH] feat(inbox): render expandable inbox report card under initial prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a task is associated with a PostHog inbox report (task.signal_report), the conversation now renders an expandable report card directly under the initial prompt, so the report can be read inline instead of navigating away to the inbox view. - New InboxReportCard component fetches the report via the existing useInboxReportById hook (shared query cache, stays in sync). Collapsed shows title + status badge; expanded shows the researched summary, priority/actionability badges, and an "Open in inbox" action. - UserMessage gains an optional signalReportId prop that renders the card below the message body, mirroring the existing slackThreadUrl pattern. - ConversationView passes task.signal_report to the first user message only. Rebased onto main to resolve conflicts from the apps/code/src/renderer → packages/ui feature relocation; component now lives under packages/ui. Generated-By: PostHog Code Task-Id: c0568add-708f-41eb-a334-563d52b70afa --- .../inbox/components/InboxReportCard.tsx | 120 ++++++++++++++++++ .../sessions/components/ConversationView.tsx | 14 +- .../components/session-update/UserMessage.tsx | 6 + 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 packages/ui/src/features/inbox/components/InboxReportCard.tsx diff --git a/packages/ui/src/features/inbox/components/InboxReportCard.tsx b/packages/ui/src/features/inbox/components/InboxReportCard.tsx new file mode 100644 index 0000000000..ec051514ab --- /dev/null +++ b/packages/ui/src/features/inbox/components/InboxReportCard.tsx @@ -0,0 +1,120 @@ +import { + ArrowSquareOut, + CaretDown, + CaretUp, + Tray, +} from "@phosphor-icons/react"; +import { useInboxReportById } from "@posthog/ui/features/inbox/hooks/useInboxReports"; +import { useInboxReportSelectionStore } from "@posthog/ui/features/inbox/stores/inboxReportSelectionStore"; +import { useInboxSignalsFilterStore } from "@posthog/ui/features/inbox/stores/inboxSignalsFilterStore"; +import { navigateToInbox } from "@posthog/ui/router/navigationBridge"; +import { Box, Flex, Spinner, Text } from "@radix-ui/themes"; +import { useCallback, useState } from "react"; +import { SignalReportActionabilityBadge } from "./utils/SignalReportActionabilityBadge"; +import { SignalReportPriorityBadge } from "./utils/SignalReportPriorityBadge"; +import { SignalReportStatusBadge } from "./utils/SignalReportStatusBadge"; +import { SignalReportSummaryMarkdown } from "./utils/SignalReportSummaryMarkdown"; + +interface InboxReportCardProps { + reportId: string; +} + +/** + * Compact, expandable card for the inbox report a task is associated with. + * Rendered under the initial prompt so the report can be read inline instead + * of navigating away to the inbox view. Reads the report from the same query + * cache the inbox uses (`useInboxReportById`), so it stays in sync and an + * "Open in inbox" action can reuse the warmed cache for the detail pane. + */ +export function InboxReportCard({ reportId }: InboxReportCardProps) { + const [expanded, setExpanded] = useState(false); + const { data: report, isLoading } = useInboxReportById(reportId, { + staleTime: 60_000, + }); + + const setSelectedReportIds = useInboxReportSelectionStore( + (s) => s.setSelectedReportIds, + ); + const resetFilters = useInboxSignalsFilterStore((s) => s.resetFilters); + + const handleOpenInInbox = useCallback(() => { + // Reset inbox-local filters first so the report isn't hidden by an active + // filter, then navigate and select it (mirrors the deep-link open path). + resetFilters(); + navigateToInbox(); + setSelectedReportIds([reportId]); + }, [reportId, resetFilters, setSelectedReportIds]); + + if (isLoading && !report) { + return ( + + + + Loading inbox report... + + + ); + } + + if (!report) return null; + + return ( + + + + {expanded && ( + + + + {(report.priority || report.actionability) && ( + + + + + )} + + + + )} + + ); +} diff --git a/packages/ui/src/features/sessions/components/ConversationView.tsx b/packages/ui/src/features/sessions/components/ConversationView.tsx index d15543515e..f4b8da6392 100644 --- a/packages/ui/src/features/sessions/components/ConversationView.tsx +++ b/packages/ui/src/features/sessions/components/ConversationView.tsx @@ -258,6 +258,11 @@ export function ConversationView({ ? slackThreadUrl : undefined } + signalReportId={ + item.id === firstUserMessageId + ? (task?.signal_report ?? undefined) + : undefined + } /> ); case "git_action": @@ -286,7 +291,14 @@ export function ConversationView({ return ; } }, - [repoPath, taskId, slackThreadUrl, firstUserMessageId, initialItemIds], + [ + repoPath, + taskId, + slackThreadUrl, + firstUserMessageId, + initialItemIds, + task?.signal_report, + ], ); const getRowKey = useCallback((row: ThreadRow) => row.id, []); diff --git a/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx b/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx index bb168abf39..fd09bb9d4e 100644 --- a/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx +++ b/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx @@ -15,6 +15,7 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Tooltip } from "../../../../primitives/Tooltip"; import { MarkdownRenderer } from "../../../editor/components/MarkdownRenderer"; import { useFeatureFlag } from "../../../feature-flags/useFeatureFlag"; +import { InboxReportCard } from "../../../inbox/components/InboxReportCard"; import { usePanelLayoutStore } from "../../../panels/panelLayoutStore"; import type { UserMessageAttachment } from "../../userMessageTypes"; import { extractCanvasInstructions } from "./canvasInstructions"; @@ -32,6 +33,9 @@ interface UserMessageProps { content: string; timestamp?: number; sourceUrl?: string; + /** Inbox report this message's task is associated with — rendered as an + * expandable card under the message (first user message only). */ + signalReportId?: string; attachments?: UserMessageAttachment[]; animate?: boolean; /** Task the message belongs to — needed to open the context file tab. */ @@ -58,6 +62,7 @@ export const UserMessage = memo(function UserMessage({ content, timestamp, sourceUrl, + signalReportId, attachments = [], animate = true, taskId, @@ -251,6 +256,7 @@ export const UserMessage = memo(function UserMessage({ View Slack thread )} + {signalReportId && } {timestamp != null && (