diff --git a/README.md b/README.md index d29bf7c..deb8554 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ One Docker container. OTLP ingest on `:4318`, interactive analytics dashboard on ## What you get - **Overview dashboard** — KPI cards for sessions, unique users, total cost, and token counts (30-day window), with per-user filter across all charts -- **Sessions** — live table of every Claude Code session with model, duration, cost, and status (OK / ERROR) +- **Sessions** — live table of every Claude Code session with user, model, duration, cost, and status (OK / ERROR); search by user and click any user to filter the table to their sessions - **History** — time-series and daily-activity heatmaps for sessions and token spend over time - **Costs** — cumulative spend chart + breakdown table by model - **Tools** — call counts, average duration, and error rate per tool (`Bash`, `Read`, `Edit`, …) diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 6c3fcdb..36b52e7 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -20,6 +20,7 @@ export interface OverviewResponse { export interface SessionItem { session_id: string + user_id: string first_seen: string last_seen: string model: string diff --git a/frontend/src/pages/Sessions.module.css b/frontend/src/pages/Sessions.module.css index d61acc4..19ffdbc 100644 --- a/frontend/src/pages/Sessions.module.css +++ b/frontend/src/pages/Sessions.module.css @@ -85,6 +85,26 @@ color: var(--color-accent); } +.userLink { + background: none; + border: none; + padding: 0; + margin: 0; + cursor: pointer; + color: var(--color-accent); + font: inherit; + text-align: left; +} + +.userLink:hover { + text-decoration: underline; +} + +.anonUser { + color: var(--color-text-2); + font-style: italic; +} + .pagination { display: flex; align-items: center; diff --git a/frontend/src/pages/Sessions.tsx b/frontend/src/pages/Sessions.tsx index 8f5d6a0..191f789 100644 --- a/frontend/src/pages/Sessions.tsx +++ b/frontend/src/pages/Sessions.tsx @@ -22,11 +22,21 @@ export default function Sessions() { }) } + function applyUserFilter(uid: string) { + setSearchParams((prev) => { + const next = new URLSearchParams(prev) + next.set('user_id', uid) + return next + }) + setPage(1) + } + const filtered = useMemo(() => { if (!data) return [] + const needle = search.toLowerCase() return data.items.filter( (s) => - (search === '' || s.session_id.toLowerCase().includes(search.toLowerCase())) && + (search === '' || (s.user_id ?? '').toLowerCase().includes(needle)) && (statusFilter === 'all' || s.status === statusFilter), ) }, [data, search, statusFilter]) @@ -47,7 +57,7 @@ export default function Sessions() {