11import { ResizableSidebar } from "@components/ResizableSidebar" ;
22import { useAuthStore } from "@features/auth/stores/authStore" ;
3+ import { InboxLiveRail } from "@features/inbox/components/InboxLiveRail" ;
34import {
45 useInboxReportArtefacts ,
56 useInboxReportSignals ,
@@ -9,10 +10,15 @@ import { useInboxCloudTaskStore } from "@features/inbox/stores/inboxCloudTaskSto
910import { useInboxSignalsFilterStore } from "@features/inbox/stores/inboxSignalsFilterStore" ;
1011import { useInboxSignalsSidebarStore } from "@features/inbox/stores/inboxSignalsSidebarStore" ;
1112import { buildSignalTaskPrompt } from "@features/inbox/utils/buildSignalTaskPrompt" ;
13+ import { filterReportsBySearch } from "@features/inbox/utils/filterReports" ;
1214import {
13- buildOrdering ,
14- filterReportsBySearch ,
15- } from "@features/inbox/utils/filterReports" ;
15+ INBOX_PIPELINE_STATUS_FILTER ,
16+ INBOX_REFETCH_INTERVAL_MS ,
17+ } from "@features/inbox/utils/inboxConstants" ;
18+ import {
19+ isReportActionable ,
20+ sortInboxPipelineReports ,
21+ } from "@features/inbox/utils/inboxSort" ;
1622import { useDraftStore } from "@features/message-editor/stores/draftStore" ;
1723import { useCreateTask } from "@features/tasks/hooks/useTasks" ;
1824import { useFeatureFlag } from "@hooks/useFeatureFlag" ;
@@ -21,7 +27,6 @@ import {
2127 ArrowSquareOutIcon ,
2228 ClockIcon ,
2329 Cloud as CloudIcon ,
24- SparkleIcon ,
2530 XIcon ,
2631} from "@phosphor-icons/react" ;
2732import {
@@ -40,13 +45,22 @@ import type {
4045 SignalReportsQueryParams ,
4146} from "@shared/types" ;
4247import { useNavigationStore } from "@stores/navigationStore" ;
48+ import { useRendererWindowFocusStore } from "@stores/rendererWindowFocusStore" ;
4349import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
4450import { toast } from "sonner" ;
4551import { SignalsErrorState , SignalsLoadingState } from "./InboxEmptyStates" ;
52+ import { InboxWarmingUpState } from "./InboxWarmingUpState" ;
4653import { ReportCard } from "./ReportCard" ;
4754import { SignalCard } from "./SignalCard" ;
55+ import { SignalReportPriorityBadge } from "./SignalReportPriorityBadge" ;
56+ import { SignalReportSummaryMarkdown } from "./SignalReportSummaryMarkdown" ;
4857import { SignalsToolbar } from "./SignalsToolbar" ;
4958
59+ const INBOX_QUERY_PARAMS : SignalReportsQueryParams = {
60+ status : INBOX_PIPELINE_STATUS_FILTER ,
61+ ordering : "-updated_at" ,
62+ } ;
63+
5064function getArtefactsUnavailableMessage (
5165 reason : SignalReportArtefactsResponse [ "unavailableReason" ] ,
5266) : string {
@@ -109,13 +123,9 @@ export function InboxSignalsTab() {
109123 const sortDirection = useInboxSignalsFilterStore ( ( s ) => s . sortDirection ) ;
110124 const searchQuery = useInboxSignalsFilterStore ( ( s ) => s . searchQuery ) ;
111125
112- const queryParams = useMemo < SignalReportsQueryParams > (
113- ( ) => ( {
114- status : "ready" ,
115- ordering : buildOrdering ( sortField , sortDirection ) ,
116- } ) ,
117- [ sortField , sortDirection ] ,
118- ) ;
126+ const windowFocused = useRendererWindowFocusStore ( ( s ) => s . focused ) ;
127+ const isInboxView = useNavigationStore ( ( s ) => s . view . type === "inbox" ) ;
128+ const inboxPollingActive = windowFocused && isInboxView ;
119129
120130 const {
121131 allReports,
@@ -127,10 +137,23 @@ export function InboxSignalsTab() {
127137 hasNextPage,
128138 isFetchingNextPage,
129139 fetchNextPage,
130- } = useInboxReportsInfinite ( queryParams ) ;
131- const reports = useMemo (
132- ( ) => filterReportsBySearch ( allReports , searchQuery ) ,
133- [ allReports , searchQuery ] ,
140+ } = useInboxReportsInfinite ( INBOX_QUERY_PARAMS , {
141+ refetchInterval : inboxPollingActive ? INBOX_REFETCH_INTERVAL_MS : false ,
142+ refetchIntervalInBackground : false ,
143+ staleTime : inboxPollingActive ? INBOX_REFETCH_INTERVAL_MS : 12_000 ,
144+ } ) ;
145+ const reports = useMemo ( ( ) => {
146+ const searched = filterReportsBySearch ( allReports , searchQuery ) ;
147+ return sortInboxPipelineReports ( searched , sortField , sortDirection ) ;
148+ } , [ allReports , searchQuery , sortField , sortDirection ] ) ;
149+
150+ const readyCount = useMemo (
151+ ( ) => allReports . filter ( ( r ) => r . status === "ready" ) . length ,
152+ [ allReports ] ,
153+ ) ;
154+ const processingCount = useMemo (
155+ ( ) => allReports . filter ( ( r ) => r . status !== "ready" ) . length ,
156+ [ allReports ] ,
134157 ) ;
135158 const [ selectedReportId , setSelectedReportId ] = useState < string | null > ( null ) ;
136159 const sidebarOpen = useInboxSignalsSidebarStore ( ( state ) => state . open ) ;
@@ -185,6 +208,9 @@ export function InboxSignalsTab() {
185208 } ) ;
186209 const signals = signalsQuery . data ?. signals ?? [ ] ;
187210
211+ const canActOnReport =
212+ ! ! selectedReport && isReportActionable ( selectedReport . status ) ;
213+
188214 const cloudRegion = useAuthStore ( ( state ) => state . cloudRegion ) ;
189215 const projectId = useAuthStore ( ( state ) => state . projectId ) ;
190216 const replayBaseUrl =
@@ -217,6 +243,9 @@ export function InboxSignalsTab() {
217243 } , [ selectedReport , visibleArtefacts , signals , replayBaseUrl ] ) ;
218244
219245 const handleCreateTask = ( ) => {
246+ if ( ! selectedReport || ! isReportActionable ( selectedReport . status ) ) {
247+ return ;
248+ }
220249 const prompt = buildPrompt ( ) ;
221250 if ( ! prompt ) return ;
222251
@@ -230,14 +259,21 @@ export function InboxSignalsTab() {
230259 openCloudConfirm ( repositories [ 0 ] ?? null ) ;
231260 } , [ repositories , openCloudConfirm ] ) ;
232261
262+ const selectedReportRef = useRef ( selectedReport ) ;
263+ selectedReportRef . current = selectedReport ;
264+
233265 const handleRunCloudTask = useCallback ( async ( ) => {
266+ const report = selectedReportRef . current ;
267+ if ( ! report || ! isReportActionable ( report . status ) ) {
268+ return ;
269+ }
234270 const prompt = buildPrompt ( ) ;
235271 if ( ! prompt ) return ;
236272
237273 const result = await runCloudTask ( {
238274 prompt,
239275 githubIntegrationId : githubIntegration ?. id ,
240- reportId : selectedReport ? .id ,
276+ reportId : report . id ,
241277 } ) ;
242278
243279 if ( result . success && result . task ) {
@@ -251,7 +287,6 @@ export function InboxSignalsTab() {
251287 runCloudTask ,
252288 invalidateTasks ,
253289 navigateToTask ,
254- selectedReport ?. id ,
255290 githubIntegration ?. id ,
256291 ] ) ;
257292
@@ -271,29 +306,7 @@ export function InboxSignalsTab() {
271306 }
272307
273308 if ( allReports . length === 0 ) {
274- return (
275- < Flex
276- direction = "column"
277- align = "center"
278- justify = "center"
279- gap = "3"
280- height = "100%"
281- className = "text-center"
282- >
283- < SparkleIcon size = { 24 } className = "text-gray-8" />
284- < Text size = "2" weight = "medium" className = "font-mono text-[12px]" >
285- No signals yet
286- </ Text >
287- < Text
288- size = "1"
289- color = "gray"
290- className = "font-mono text-[11px]"
291- style = { { maxWidth : 520 } }
292- >
293- Signals are processing. Check back soon as fresh events arrive.
294- </ Text >
295- </ Flex >
296- ) ;
309+ return < InboxWarmingUpState /> ;
297310 }
298311
299312 return (
@@ -305,10 +318,14 @@ export function InboxSignalsTab() {
305318 style = { { height : "100%" } }
306319 >
307320 < Flex direction = "column" >
321+ < InboxLiveRail active = { inboxPollingActive } />
308322 < SignalsToolbar
309323 totalCount = { totalCount }
310324 filteredCount = { reports . length }
311325 isSearchActive = { ! ! searchQuery . trim ( ) }
326+ livePolling = { inboxPollingActive }
327+ readyCount = { readyCount }
328+ processingCount = { processingCount }
312329 />
313330 { reports . length === 0 && searchQuery . trim ( ) ? (
314331 < Flex
@@ -323,9 +340,10 @@ export function InboxSignalsTab() {
323340 </ Text >
324341 </ Flex >
325342 ) : null }
326- { reports . map ( ( report ) => (
343+ { reports . map ( ( report , index ) => (
327344 < ReportCard
328345 key = { report . id }
346+ index = { index }
329347 report = { report }
330348 isSelected = { selectedReport ?. id === report . id }
331349 onClick = { ( ) => {
@@ -379,11 +397,12 @@ export function InboxSignalsTab() {
379397 < XIcon size = { 14 } />
380398 </ button >
381399 </ Flex >
382- < Flex align = "center" gap = "1" >
400+ < Flex align = "center" gap = "1" wrap = "wrap" >
383401 < Button
384402 size = "1"
385403 variant = "soft"
386404 onClick = { handleCreateTask }
405+ disabled = { ! canActOnReport }
387406 className = "font-mono text-[11px]"
388407 >
389408 Create task
@@ -393,14 +412,29 @@ export function InboxSignalsTab() {
393412 size = "1"
394413 variant = "solid"
395414 onClick = { handleOpenCloudConfirm }
396- disabled = { isRunningCloudTask || repositories . length === 0 }
415+ disabled = {
416+ ! canActOnReport ||
417+ isRunningCloudTask ||
418+ repositories . length === 0
419+ }
397420 className = "font-mono text-[11px]"
398421 >
399422 < CloudIcon size = { 12 } />
400423 { isRunningCloudTask ? "Running..." : "Run cloud" }
401424 </ Button >
402425 ) }
403426 </ Flex >
427+ { ! canActOnReport && selectedReport ? (
428+ < Text
429+ size = "1"
430+ color = "gray"
431+ className = "font-mono text-[10px] leading-snug"
432+ >
433+ { selectedReport . status === "pending_input"
434+ ? "This report needs input in PostHog before an agent can act on it."
435+ : "Research is still running — you can read context below, then create a task when status is Ready." }
436+ </ Text >
437+ ) : null }
404438 </ Flex >
405439 < ScrollArea
406440 type = "auto"
@@ -409,14 +443,13 @@ export function InboxSignalsTab() {
409443 style = { { height : "calc(100% - 41px)" } }
410444 >
411445 < Flex direction = "column" gap = "2" p = "2" className = "min-w-0" >
412- < Text
413- size = "1"
414- color = "gray"
415- className = "whitespace-pre-wrap text-pretty break-words font-mono text-[11px]"
416- >
417- { selectedReport . summary ?? "No summary available." }
418- </ Text >
446+ < SignalReportSummaryMarkdown
447+ content = { selectedReport . summary }
448+ fallback = "No summary available."
449+ variant = "detail"
450+ />
419451 < Flex align = "center" gap = "2" wrap = "wrap" >
452+ < SignalReportPriorityBadge priority = { selectedReport . priority } />
420453 < Badge variant = "soft" color = "gray" size = "1" >
421454 { selectedReport . signal_count } occurrences
422455 </ Badge >
0 commit comments