Skip to content

Commit c06bcae

Browse files
joshsnycursoragent
andauthored
feat(inbox): update signals api in inbox + improve ui for showing signals (#983)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent 461fe02 commit c06bcae

File tree

7 files changed

+452
-10
lines changed

7 files changed

+452
-10
lines changed

apps/twig/src/renderer/api/posthogClient.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type {
33
RepoAutonomyStatus,
44
SignalReportArtefact,
55
SignalReportArtefactsResponse,
6+
SignalReportSignalsResponse,
7+
SignalReportsQueryParams,
68
SignalReportsResponse,
79
Task,
810
TaskRun,
@@ -606,11 +608,27 @@ export class PostHogAPIClient {
606608
}));
607609
}
608610

609-
async getSignalReports(): Promise<SignalReportsResponse> {
611+
async getSignalReports(
612+
params?: SignalReportsQueryParams,
613+
): Promise<SignalReportsResponse> {
610614
const teamId = await this.getTeamId();
611615
const url = new URL(
612616
`${this.api.baseUrl}/api/projects/${teamId}/signal_reports/`,
613617
);
618+
619+
if (params?.limit != null) {
620+
url.searchParams.set("limit", String(params.limit));
621+
}
622+
if (params?.offset != null) {
623+
url.searchParams.set("offset", String(params.offset));
624+
}
625+
if (params?.status) {
626+
url.searchParams.set("status", params.status);
627+
}
628+
if (params?.ordering) {
629+
url.searchParams.set("ordering", params.ordering);
630+
}
631+
614632
const response = await this.api.fetcher.fetch({
615633
method: "get",
616634
url,
@@ -628,6 +646,39 @@ export class PostHogAPIClient {
628646
};
629647
}
630648

649+
async getSignalReportSignals(
650+
reportId: string,
651+
): Promise<SignalReportSignalsResponse> {
652+
try {
653+
const teamId = await this.getTeamId();
654+
const url = new URL(
655+
`${this.api.baseUrl}/api/projects/${teamId}/signal_reports/${reportId}/signals/`,
656+
);
657+
const response = await this.api.fetcher.fetch({
658+
method: "get",
659+
url,
660+
path: `/api/projects/${teamId}/signal_reports/${reportId}/signals/`,
661+
});
662+
663+
if (!response.ok) {
664+
log.warn("Signal report signals unavailable", {
665+
reportId,
666+
status: response.status,
667+
});
668+
return { report: null, signals: [] };
669+
}
670+
671+
const data = await response.json();
672+
return {
673+
report: data.report ?? null,
674+
signals: data.signals ?? [],
675+
};
676+
} catch (error) {
677+
log.warn("Failed to fetch signal report signals", { reportId, error });
678+
return { report: null, signals: [] };
679+
}
680+
}
681+
631682
async getSignalReportArtefacts(
632683
reportId: string,
633684
): Promise<SignalReportArtefactsResponse> {

apps/twig/src/renderer/features/inbox/components/InboxSignalsTab.tsx

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ResizableSidebar } from "@components/ResizableSidebar";
22
import { useAuthStore } from "@features/auth/stores/authStore";
33
import {
44
useInboxReportArtefacts,
5+
useInboxReportSignals,
56
useInboxReports,
67
} from "@features/inbox/hooks/useInboxReports";
78
import { useInboxCloudTaskStore } from "@features/inbox/stores/inboxCloudTaskStore";
@@ -35,6 +36,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
3536
import { toast } from "sonner";
3637
import { SignalsErrorState, SignalsLoadingState } from "./InboxEmptyStates";
3738
import { ReportCard } from "./ReportCard";
39+
import { SignalCard } from "./SignalCard";
3840

3941
interface InboxSignalsTabProps {
4042
onGoToSetup: () => void;
@@ -58,7 +60,9 @@ function getArtefactsUnavailableMessage(
5860
}
5961

6062
export function InboxSignalsTab({ onGoToSetup }: InboxSignalsTabProps) {
61-
const { data, isLoading, isFetching, error, refetch } = useInboxReports();
63+
const { data, isLoading, isFetching, error, refetch } = useInboxReports({
64+
status: "ready",
65+
});
6266
const reports = data?.results ?? [];
6367
const [selectedReportId, setSelectedReportId] = useState<string | null>(null);
6468
const sidebarOpen = useInboxSignalsSidebarStore((state) => state.open);
@@ -108,6 +112,11 @@ export function InboxSignalsTab({ onGoToSetup }: InboxSignalsTabProps) {
108112
? "Evidence could not be loaded right now. You can still create a task from this report."
109113
: getArtefactsUnavailableMessage(artefactsUnavailableReason);
110114

115+
const signalsQuery = useInboxReportSignals(selectedReport?.id ?? "", {
116+
enabled: !!selectedReport,
117+
});
118+
const signals = signalsQuery.data?.signals ?? [];
119+
111120
const cloudRegion = useAuthStore((state) => state.cloudRegion);
112121
const projectId = useAuthStore((state) => state.projectId);
113122
const replayBaseUrl =
@@ -134,9 +143,10 @@ export function InboxSignalsTab({ onGoToSetup }: InboxSignalsTabProps) {
134143
return buildSignalTaskPrompt({
135144
report: selectedReport,
136145
artefacts: visibleArtefacts,
146+
signals,
137147
replayBaseUrl,
138148
});
139-
}, [selectedReport, visibleArtefacts, replayBaseUrl]);
149+
}, [selectedReport, visibleArtefacts, signals, replayBaseUrl]);
140150

141151
const handleCreateTask = () => {
142152
const prompt = buildPrompt();
@@ -336,6 +346,33 @@ export function InboxSignalsTab({ onGoToSetup }: InboxSignalsTabProps) {
336346
</Badge>
337347
</Flex>
338348

349+
{signals.length > 0 && (
350+
<Box>
351+
<Text
352+
size="1"
353+
weight="medium"
354+
className="block font-mono text-[12px]"
355+
mb="2"
356+
>
357+
Signals ({signals.length})
358+
</Text>
359+
<Flex direction="column" gap="2">
360+
{signals.map((signal) => (
361+
<SignalCard key={signal.signal_id} signal={signal} />
362+
))}
363+
</Flex>
364+
</Box>
365+
)}
366+
{signalsQuery.isLoading && (
367+
<Text
368+
size="1"
369+
color="gray"
370+
className="block font-mono text-[11px]"
371+
>
372+
Loading signals...
373+
</Text>
374+
)}
375+
339376
<Box>
340377
<Text
341378
size="1"

0 commit comments

Comments
 (0)