Skip to content

Commit bec6af1

Browse files
committed
feat(inbox): error tracking source, LLM evaluations, source config improvements
- Add error tracking as signal source with 3 sub-types toggled together - Replace non-functional LLM analytics toggle with evaluations list (polls 5s) - Evaluations link to Cloud for management (region-aware) - Fix re-render cascade: direct API calls, per-source optimistic state - Per-source onToggle API with memoized cards - Rounded toggle cards, GitHub OAuth flow - Add suggested reviewer + artefact types
1 parent 3325bf5 commit bec6af1

11 files changed

Lines changed: 642 additions & 231 deletions

File tree

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

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,25 @@ export type McpRecommendedServer = Schemas.RecommendedServer;
2020

2121
export type McpServerInstallation = Schemas.MCPServerInstallation;
2222

23+
export type Evaluation = Schemas.Evaluation;
24+
2325
export interface SignalSourceConfig {
2426
id: string;
2527
source_product:
2628
| "session_replay"
2729
| "llm_analytics"
2830
| "github"
2931
| "linear"
30-
| "zendesk";
31-
source_type: "session_analysis_cluster" | "evaluation" | "issue" | "ticket";
32+
| "zendesk"
33+
| "error_tracking";
34+
source_type:
35+
| "session_analysis_cluster"
36+
| "evaluation"
37+
| "issue"
38+
| "ticket"
39+
| "issue_created"
40+
| "issue_reopened"
41+
| "issue_spiking";
3242
enabled: boolean;
3343
config: Record<string, unknown>;
3444
created_at: string;
@@ -223,17 +233,8 @@ export class PostHogAPIClient {
223233
async createSignalSourceConfig(
224234
projectId: number,
225235
options: {
226-
source_product:
227-
| "session_replay"
228-
| "llm_analytics"
229-
| "github"
230-
| "linear"
231-
| "zendesk";
232-
source_type:
233-
| "session_analysis_cluster"
234-
| "evaluation"
235-
| "issue"
236-
| "ticket";
236+
source_product: SignalSourceConfig["source_product"];
237+
source_type: SignalSourceConfig["source_type"];
237238
enabled: boolean;
238239
config?: Record<string, unknown>;
239240
},
@@ -287,6 +288,34 @@ export class PostHogAPIClient {
287288
return (await response.json()) as SignalSourceConfig;
288289
}
289290

291+
async listEvaluations(projectId: number): Promise<Evaluation[]> {
292+
const data = await this.api.get(
293+
"/api/environments/{project_id}/evaluations/",
294+
{
295+
path: { project_id: projectId.toString() },
296+
query: { limit: 200 },
297+
},
298+
);
299+
return data.results ?? [];
300+
}
301+
302+
async updateEvaluation(
303+
projectId: number,
304+
evaluationId: string,
305+
updates: { enabled: boolean },
306+
): Promise<Evaluation> {
307+
return await this.api.patch(
308+
"/api/environments/{project_id}/evaluations/{id}/",
309+
{
310+
path: {
311+
project_id: projectId.toString(),
312+
id: evaluationId,
313+
},
314+
body: updates,
315+
},
316+
);
317+
}
318+
290319
async listExternalDataSources(
291320
projectId: number,
292321
): Promise<ExternalDataSource[]> {

apps/code/src/renderer/features/inbox/components/DataSourceSetup.tsx

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,41 @@ interface SetupFormProps {
5252
onCancel: () => void;
5353
}
5454

55+
const POLL_INTERVAL_GITHUB_MS = 3_000;
56+
const POLL_TIMEOUT_GITHUB_MS = 300_000;
57+
5558
function GitHubSetup({ onComplete, onCancel }: SetupFormProps) {
5659
const projectId = useAuthStateValue((state) => state.projectId);
60+
const cloudRegion = useAuthStateValue((state) => state.cloudRegion);
5761
const client = useAuthenticatedClient();
5862
const { githubIntegration, repositories, isLoadingRepos } =
5963
useRepositoryIntegration();
6064
const [repo, setRepo] = useState<string | null>(null);
6165
const [loading, setLoading] = useState(false);
66+
const [connecting, setConnecting] = useState(false);
67+
const pollTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
68+
const pollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
69+
70+
const stopPolling = useCallback(() => {
71+
if (pollTimerRef.current) {
72+
clearInterval(pollTimerRef.current);
73+
pollTimerRef.current = null;
74+
}
75+
if (pollTimeoutRef.current) {
76+
clearTimeout(pollTimeoutRef.current);
77+
pollTimeoutRef.current = null;
78+
}
79+
}, []);
80+
81+
useEffect(() => stopPolling, [stopPolling]);
82+
83+
// Stop polling once integration appears
84+
useEffect(() => {
85+
if (githubIntegration && connecting) {
86+
stopPolling();
87+
setConnecting(false);
88+
}
89+
}, [githubIntegration, connecting, stopPolling]);
6290

6391
// Auto-select the first repo once loaded
6492
useEffect(() => {
@@ -67,6 +95,47 @@ function GitHubSetup({ onComplete, onCancel }: SetupFormProps) {
6795
}
6896
}, [repo, repositories]);
6997

98+
const handleConnectGitHub = useCallback(async () => {
99+
if (!cloudRegion || !projectId) return;
100+
setConnecting(true);
101+
try {
102+
await trpcClient.githubIntegration.startFlow.mutate({
103+
region: cloudRegion,
104+
projectId,
105+
});
106+
107+
pollTimerRef.current = setInterval(async () => {
108+
try {
109+
if (!client) return;
110+
// Trigger a refetch of integrations
111+
const integrations =
112+
await client.getIntegrationsForProject(projectId);
113+
const hasGithub = integrations.some(
114+
(i: { kind: string }) => i.kind === "github",
115+
);
116+
if (hasGithub) {
117+
stopPolling();
118+
setConnecting(false);
119+
toast.success("GitHub connected");
120+
}
121+
} catch {
122+
// Ignore individual poll failures
123+
}
124+
}, POLL_INTERVAL_GITHUB_MS);
125+
126+
pollTimeoutRef.current = setTimeout(() => {
127+
stopPolling();
128+
setConnecting(false);
129+
toast.error("Connection timed out. Please try again.");
130+
}, POLL_TIMEOUT_GITHUB_MS);
131+
} catch (error) {
132+
setConnecting(false);
133+
toast.error(
134+
error instanceof Error ? error.message : "Failed to start GitHub flow",
135+
);
136+
}
137+
}, [cloudRegion, projectId, client, stopPolling]);
138+
70139
const handleSubmit = useCallback(async () => {
71140
if (!projectId || !client || !repo || !githubIntegration) return;
72141

@@ -97,10 +166,28 @@ function GitHubSetup({ onComplete, onCancel }: SetupFormProps) {
97166
if (!githubIntegration) {
98167
return (
99168
<SetupFormContainer title="Connect GitHub">
100-
<Text size="2" style={{ color: "var(--gray-11)" }}>
101-
No GitHub integration found. Please connect GitHub during onboarding
102-
first.
103-
</Text>
169+
<Flex direction="column" gap="3">
170+
<Text size="2" style={{ color: "var(--gray-11)" }}>
171+
Connect your GitHub account to import issues as signals.
172+
</Text>
173+
<Flex gap="2" justify="end">
174+
<Button
175+
size="2"
176+
variant="soft"
177+
onClick={onCancel}
178+
disabled={connecting}
179+
>
180+
Cancel
181+
</Button>
182+
<Button
183+
size="2"
184+
onClick={() => void handleConnectGitHub()}
185+
disabled={connecting}
186+
>
187+
{connecting ? "Waiting for authorization..." : "Connect GitHub"}
188+
</Button>
189+
</Flex>
190+
</Flex>
104191
</SetupFormContainer>
105192
);
106193
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
} from "@radix-ui/themes";
4343
import { getCloudUrlFromRegion } from "@shared/constants/oauth";
4444
import type {
45+
SignalReportArtefact,
4546
SignalReportArtefactsResponse,
4647
SignalReportsQueryParams,
4748
} from "@shared/types";
@@ -201,7 +202,9 @@ export function InboxSignalsTab() {
201202
const artefactsQuery = useInboxReportArtefacts(selectedReport?.id ?? "", {
202203
enabled: !!selectedReport,
203204
});
204-
const visibleArtefacts = artefactsQuery.data?.results ?? [];
205+
const visibleArtefacts = (artefactsQuery.data?.results ?? []).filter(
206+
(a): a is SignalReportArtefact => a.type !== "suggested_reviewers",
207+
);
205208
const artefactsUnavailableReason = artefactsQuery.data?.unavailableReason;
206209
const showArtefactsUnavailable =
207210
!artefactsQuery.isLoading &&

0 commit comments

Comments
 (0)