Skip to content

Commit 49113c4

Browse files
committed
Vibes: add debug sessions page
1 parent a8de148 commit 49113c4

File tree

4 files changed

+213
-9
lines changed

4 files changed

+213
-9
lines changed

internal/dev_server/ui/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import RouteSelector from './RouteSelector.tsx';
55
import FlagsPage from './FlagsPage.tsx';
66
import EventsPage from './EventsPage.tsx';
77
import DebugSessionsPage from './DebugSessionsPage.tsx';
8+
import DebugSessionEventsPage from './DebugSessionEventsPage.tsx';
89

910
function App() {
1011
return (
@@ -35,6 +36,7 @@ function App() {
3536
<Route path="/ui/flags" element={<FlagsPage />} />
3637
<Route path="/ui/events" element={<EventsPage />} />
3738
<Route path="/ui/debug-sessions" element={<DebugSessionsPage />} />
39+
<Route path="/ui/debug-sessions/:debugSessionKey/events" element={<DebugSessionEventsPage />} />
3840
</Routes>
3941
</Box>
4042
</Box>
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import { useEffect, useState } from "react";
2+
import { useParams, useNavigate } from "react-router";
3+
import { apiRoute } from "./util";
4+
import { ApiEventsPage, EventData, convertApiEventToEventData } from "./types";
5+
import { Box, Alert } from "@launchpad-ui/core";
6+
import { Heading, Text, ProgressBar, Button } from "@launchpad-ui/components";
7+
import { Icon } from "@launchpad-ui/icons";
8+
import EventsTable from "./EventsTable";
9+
10+
const DebugSessionEventsPage = () => {
11+
const { debugSessionKey } = useParams<{ debugSessionKey: string }>();
12+
const navigate = useNavigate();
13+
const [events, setEvents] = useState<EventData[]>([]);
14+
const [loading, setLoading] = useState<boolean>(true);
15+
const [error, setError] = useState<string | null>(null);
16+
const [totalCount, setTotalCount] = useState<number>(0);
17+
18+
const fetchEvents = async () => {
19+
if (!debugSessionKey) {
20+
setError("Debug session key is required");
21+
setLoading(false);
22+
return;
23+
}
24+
25+
try {
26+
setLoading(true);
27+
setError(null);
28+
29+
const response = await fetch(apiRoute(`/dev/debug-sessions/${encodeURIComponent(debugSessionKey)}/events?limit=1000`));
30+
31+
if (!response.ok) {
32+
throw new Error(`Failed to fetch events: ${response.status} ${response.statusText}`);
33+
}
34+
35+
const data: ApiEventsPage = await response.json();
36+
const convertedEvents = data.events.map(convertApiEventToEventData);
37+
setEvents(convertedEvents);
38+
setTotalCount(data.total_count);
39+
} catch (err) {
40+
setError(err instanceof Error ? err.message : "An unknown error occurred");
41+
} finally {
42+
setLoading(false);
43+
}
44+
};
45+
46+
useEffect(() => {
47+
fetchEvents();
48+
}, [debugSessionKey]);
49+
50+
const handleBackToSessions = () => {
51+
navigate("/ui/debug-sessions");
52+
};
53+
54+
if (loading) {
55+
return (
56+
<Box padding="2rem">
57+
<Box display="flex" alignItems="center" marginBottom="1rem">
58+
<Button onPress={handleBackToSessions} variant="minimal">
59+
<Icon name="arrow-left" size="small" />
60+
<Text marginLeft="0.5rem">Back to Debug Sessions</Text>
61+
</Button>
62+
</Box>
63+
<Heading>Debug Session Events</Heading>
64+
<Box marginTop="1rem">
65+
<Text color="var(--lp-color-text-ui-secondary)">
66+
Session: {debugSessionKey}
67+
</Text>
68+
</Box>
69+
<Box marginTop="1rem">
70+
<ProgressBar isIndeterminate />
71+
</Box>
72+
<Box marginTop="1rem">
73+
<Text>Loading events...</Text>
74+
</Box>
75+
</Box>
76+
);
77+
}
78+
79+
if (error) {
80+
return (
81+
<Box padding="2rem">
82+
<Box display="flex" alignItems="center" marginBottom="1rem">
83+
<Button onPress={handleBackToSessions} variant="minimal">
84+
<Icon name="arrow-left" size="small" />
85+
<Text marginLeft="0.5rem">Back to Debug Sessions</Text>
86+
</Button>
87+
</Box>
88+
<Heading>Debug Session Events</Heading>
89+
<Box marginTop="1rem">
90+
<Text color="var(--lp-color-text-ui-secondary)">
91+
Session: {debugSessionKey}
92+
</Text>
93+
</Box>
94+
<Box marginTop="1rem">
95+
<Alert kind="error">
96+
<Text>Error: {error}</Text>
97+
</Alert>
98+
</Box>
99+
<Box marginTop="1rem">
100+
<Button onPress={fetchEvents}>Retry</Button>
101+
</Box>
102+
</Box>
103+
);
104+
}
105+
106+
return (
107+
<Box padding="2rem">
108+
<Box display="flex" alignItems="center" marginBottom="1rem">
109+
<Button onPress={handleBackToSessions} variant="minimal">
110+
<Icon name="arrow-left" size="small" />
111+
<Text marginLeft="0.5rem">Back to Debug Sessions</Text>
112+
</Button>
113+
</Box>
114+
115+
<Box display="flex" justifyContent="space-between" alignItems="center" marginBottom="1rem">
116+
<Box>
117+
<Heading>Debug Session Events</Heading>
118+
<Box marginTop="0.5rem">
119+
<Text color="var(--lp-color-text-ui-secondary)" style={{ fontFamily: "monospace" }}>
120+
Session: {debugSessionKey}
121+
</Text>
122+
</Box>
123+
</Box>
124+
<Text color="var(--lp-color-text-ui-secondary)">
125+
{totalCount} total event{totalCount !== 1 ? 's' : ''}
126+
</Text>
127+
</Box>
128+
129+
{events.length === 0 ? (
130+
<Box
131+
padding="2rem"
132+
textAlign="center"
133+
backgroundColor="var(--lp-color-bg-ui-secondary)"
134+
borderRadius="4px"
135+
>
136+
<Icon name="data" size="large" />
137+
<Box marginTop="1rem">
138+
<Text>No events found for this debug session</Text>
139+
</Box>
140+
<Box marginTop="0.5rem">
141+
<Text color="var(--lp-color-text-ui-secondary)">
142+
Events will appear here when they are captured for this session
143+
</Text>
144+
</Box>
145+
</Box>
146+
) : (
147+
<EventsTable events={events} />
148+
)}
149+
</Box>
150+
);
151+
};
152+
153+
export default DebugSessionEventsPage;

internal/dev_server/ui/src/DebugSessionsPage.tsx

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { useEffect, useState } from "react";
2+
import { useNavigate } from "react-router";
23
import { apiRoute } from "./util";
34
import { DebugSession, DebugSessionsPage as DebugSessionsPageType } from "./types";
45
import { Box, CopyToClipboard, Alert } from "@launchpad-ui/core";
56
import { Heading, Text, ProgressBar, Button } from "@launchpad-ui/components";
67
import { Icon } from "@launchpad-ui/icons";
78

89
const DebugSessionsPage = () => {
10+
const navigate = useNavigate();
911
const [debugSessions, setDebugSessions] = useState<DebugSession[]>([]);
1012
const [loading, setLoading] = useState<boolean>(true);
1113
const [error, setError] = useState<string | null>(null);
@@ -45,6 +47,10 @@ const DebugSessionsPage = () => {
4547
}
4648
};
4749

50+
const handleSessionClick = (sessionKey: string) => {
51+
navigate(`/ui/debug-sessions/${encodeURIComponent(sessionKey)}/events`);
52+
};
53+
4854
if (loading) {
4955
return (
5056
<Box padding="2rem">
@@ -112,13 +118,19 @@ const DebugSessionsPage = () => {
112118
<table style={{ width: "100%", borderCollapse: "collapse" }}>
113119
<thead>
114120
<tr style={{ backgroundColor: "var(--lp-color-bg-ui-secondary)" }}>
115-
<th style={{
116-
padding: "0.75rem",
117-
textAlign: "left",
121+
<th style={{
122+
padding: "0.75rem",
123+
textAlign: "left",
118124
borderBottom: "1px solid var(--lp-color-border-ui-primary)",
119125
fontWeight: 600
120126
}}>
121127
Session Key
128+
<Text
129+
color="var(--lp-color-text-ui-secondary)"
130+
style={{ fontSize: "0.75rem", fontWeight: 400, marginTop: "0.25rem" }}
131+
>
132+
(click to view events)
133+
</Text>
122134
</th>
123135
<th style={{
124136
padding: "0.75rem",
@@ -136,14 +148,14 @@ const DebugSessionsPage = () => {
136148
}}>
137149
Event Count
138150
</th>
139-
<th style={{
140-
padding: "0.75rem",
141-
textAlign: "center",
151+
<th style={{
152+
padding: "0.75rem",
153+
textAlign: "center",
142154
borderBottom: "1px solid var(--lp-color-border-ui-primary)",
143155
fontWeight: 600,
144156
width: "100px"
145157
}}>
146-
Actions
158+
Copy Key
147159
</th>
148160
</tr>
149161
</thead>
@@ -156,9 +168,22 @@ const DebugSessionsPage = () => {
156168
}}
157169
>
158170
<td style={{ padding: "0.75rem" }}>
159-
<Text style={{ fontFamily: "monospace" }}>
171+
<button
172+
onClick={() => handleSessionClick(session.key)}
173+
style={{
174+
background: "none",
175+
border: "none",
176+
padding: 0,
177+
cursor: "pointer",
178+
textAlign: "left",
179+
color: "var(--lp-color-text-link)",
180+
textDecoration: "underline",
181+
fontFamily: "monospace"
182+
}}
183+
title="Click to view events for this session"
184+
>
160185
{session.key}
161-
</Text>
186+
</button>
162187
</td>
163188
<td style={{ padding: "0.75rem" }}>
164189
<Text>

internal/dev_server/ui/src/types.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,27 @@ export interface DebugSessionsPage {
5858
total_count: number;
5959
has_more: boolean;
6060
}
61+
62+
// API Event type that matches the server response
63+
export interface ApiEvent {
64+
id: number;
65+
written_at: string;
66+
kind: string;
67+
data: any; // Raw JSON data from the API
68+
}
69+
70+
// API EventsPage type that matches the server response
71+
export interface ApiEventsPage {
72+
events: ApiEvent[];
73+
total_count: number;
74+
has_more: boolean;
75+
}
76+
77+
// Utility function to convert API event to UI EventData
78+
export function convertApiEventToEventData(apiEvent: ApiEvent): EventData {
79+
return {
80+
id: apiEvent.id.toString(),
81+
timestamp: new Date(apiEvent.written_at).getTime(),
82+
data: apiEvent.data
83+
};
84+
}

0 commit comments

Comments
 (0)