From 4fe7d09e5ce0d2bd19f3050bdb90aa5fa46c13a7 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Fri, 13 Mar 2026 13:33:45 +0000 Subject: [PATCH] feat(web): admin panel with session management (#89) Co-authored-by: Jason Woltje Co-committed-by: Jason Woltje --- apps/web/src/app/(dashboard)/admin/page.tsx | 99 +++++++++++++++++++++ apps/web/src/components/layout/sidebar.tsx | 1 + docs/TASKS.md | 4 +- 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/app/(dashboard)/admin/page.tsx diff --git a/apps/web/src/app/(dashboard)/admin/page.tsx b/apps/web/src/app/(dashboard)/admin/page.tsx new file mode 100644 index 0000000..35e6cf3 --- /dev/null +++ b/apps/web/src/app/(dashboard)/admin/page.tsx @@ -0,0 +1,99 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { api } from '@/lib/api'; + +interface SessionInfo { + id: string; + provider: string; + modelId: string; + createdAt: string; + promptCount: number; + channels: string[]; + durationMs: number; +} + +interface SessionsResponse { + sessions: SessionInfo[]; + total: number; +} + +export default function AdminPage(): React.ReactElement { + const [sessions, setSessions] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + api('/api/sessions') + .then((res) => setSessions(res.sessions)) + .catch(() => {}) + .finally(() => setLoading(false)); + }, []); + + return ( +
+

Admin

+ + {/* User Management placeholder */} +
+

User Management

+
+

+ User management will be available when the admin API is implemented +

+
+
+ + {/* Active Agent Sessions */} +
+

Active Agent Sessions

+ {loading ? ( +

Loading sessions...

+ ) : sessions.length === 0 ? ( +
+

No active sessions

+
+ ) : ( +
+ + + + + + + + + + + + {sessions.map((s) => ( + + + + + + + + ))} + +
Session IDProviderModelPromptsDuration
+ {s.id} + {s.provider}{s.modelId} + {s.promptCount} + + {formatDuration(s.durationMs)} +
+
+ )} +
+
+ ); +} + +function formatDuration(ms: number): string { + const seconds = Math.floor(ms / 1000); + if (seconds < 60) return `${seconds}s`; + const minutes = Math.floor(seconds / 60); + if (minutes < 60) return `${minutes}m ${seconds % 60}s`; + const hours = Math.floor(minutes / 60); + return `${hours}h ${minutes % 60}m`; +} diff --git a/apps/web/src/components/layout/sidebar.tsx b/apps/web/src/components/layout/sidebar.tsx index afd1a51..7d1c0a9 100644 --- a/apps/web/src/components/layout/sidebar.tsx +++ b/apps/web/src/components/layout/sidebar.tsx @@ -15,6 +15,7 @@ const navItems: NavItem[] = [ { label: 'Tasks', href: '/tasks', icon: '📋' }, { label: 'Projects', href: '/projects', icon: '📁' }, { label: 'Settings', href: '/settings', icon: '⚙️' }, + { label: 'Admin', href: '/admin', icon: '🛡️' }, ]; export function Sidebar(): React.ReactElement { diff --git a/docs/TASKS.md b/docs/TASKS.md index 7728607..adb7cc4 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -34,8 +34,8 @@ | P3-003 | done | Phase 3 | Chat UI — conversations, messages, streaming | #84 | #28 | | P3-004 | done | Phase 3 | Task management — list view + kanban board | #86 | #29 | | P3-005 | done | Phase 3 | Project & mission views — dashboard + PRD viewer | #87 | #30 | -| P3-006 | in-progress | Phase 3 | Settings — provider config, profile, integrations | — | #31 | -| P3-007 | not-started | Phase 3 | Admin panel — user management, RBAC | — | #32 | +| P3-006 | done | Phase 3 | Settings — provider config, profile, integrations | #88 | #31 | +| P3-007 | in-progress | Phase 3 | Admin panel — user management, RBAC | — | #32 | | P3-008 | not-started | Phase 3 | Verify Phase 3 — web dashboard functional E2E | — | #33 | | P4-001 | not-started | Phase 4 | @mosaic/memory — preference + insight stores | — | #34 | | P4-002 | not-started | Phase 4 | Semantic search — pgvector embeddings + search API | — | #35 |