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 000000000..ec051514a --- /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 5b1e618cc..54d347bdf 100644 --- a/packages/ui/src/features/sessions/components/ConversationView.tsx +++ b/packages/ui/src/features/sessions/components/ConversationView.tsx @@ -213,6 +213,11 @@ export function ConversationView({ ? slackThreadUrl : undefined } + signalReportId={ + item.id === firstUserMessageId + ? (task?.signal_report ?? undefined) + : undefined + } /> ); case "git_action": @@ -256,7 +261,14 @@ export function ConversationView({ ); } }, - [repoPath, taskId, slackThreadUrl, firstUserMessageId, initialItemIds], + [ + repoPath, + taskId, + slackThreadUrl, + firstUserMessageId, + initialItemIds, + task?.signal_report, + ], ); const getItemKey = useCallback((item: ConversationItem) => item.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 db0b50db4..5a950c2e8 100644 --- a/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx +++ b/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx @@ -14,6 +14,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 { extractChannelContext } from "./channelContext"; @@ -29,6 +30,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. */ @@ -55,6 +59,7 @@ export const UserMessage = memo(function UserMessage({ content, timestamp, sourceUrl, + signalReportId, attachments = [], animate = true, taskId, @@ -208,6 +213,7 @@ export const UserMessage = memo(function UserMessage({ View Slack thread )} + {signalReportId && } {timestamp != null && (