ADR-056: Container Session UI Architecture
Status
Accepted - January 5, 2026
Context
With ADR-055 (Container Session Lifecycle) implemented in the backend, we need a comprehensive frontend dashboard to:
- Visualize container sessions across Docker, Cloud Workstations, and Kubernetes
- Manage sessions with role-based access (System Admin, Tenant Admin, Team Manager, User)
- Monitor real-time heartbeat status and license utilization
- Control session lifecycle (terminate, kick users)
Problem Statement
The existing device-level session UI (B.4.4) is insufficient for:
- Multi-user containers (Cloud Workstations support 1-100 users per container)
- Container-type differentiation (Docker vs Workstation vs K8s have different behaviors)
- Hierarchical multi-tenant visibility (AZ1.AI admins see all tenants)
- Real-time heartbeat monitoring with countdown timers
Requirements
Functional:
- FR-01: System Admin views all sessions across all tenants
- FR-02: Tenant Admin views only their tenant's sessions
- FR-03: Real-time session status updates (5-second polling, WebSocket upgrade path)
- FR-04: Terminate container sessions with confirmation
- FR-05: View/kick individual users within multi-user containers
- FR-06: License utilization visualization
Non-Functional:
- NFR-01: Dashboard loads in <2s with 100 sessions
- NFR-02: Mobile responsive (320px - 1920px+)
- NFR-03: WCAG 2.1 AA accessibility compliance
- NFR-04: Offline-resilient with graceful degradation
Decision
Component Architecture
We will implement a 28-component dashboard organized into 5 layers:
┌─────────────────────────────────────────────────────────────────┐
│ CONTAINER SESSION DASHBOARD │
├─────────────────────────────────────────────────────────────────┤
│ Layer 1: Dashboard Shell │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ ContainerSessionDashboard (root orchestrator) ││
│ │ ├── DashboardHeader (breadcrumbs, role selector, refresh) ││
│ │ └── SessionMetrics (3 KPI cards) ││
│ └─────────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────────┤
│ Layer 2: Filtering & Navigation │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ SessionFilters (combined filter bar) ││
│ │ ├── TenantFilter (System Admin only) ││
│ │ ├── ContainerTypeFilter (Docker/Workstation/K8s) ││
│ │ ├── StatusFilter (Active/Released/Expired) ││
│ │ └── SearchBar (container ID, name, hostname) ││
│ └─────────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────────┤
│ Layer 3: Session List & Cards │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ ContainerSessionList (grid/table view) ││
│ │ └── ContainerSessionCard[] (individual sessions) ││
│ │ ├── SessionStatusBadge ││
│ │ ├── ContainerTypeIcon ││
│ │ ├── UserCountIndicator ││
│ │ ├── HeartbeatTimer (real-time countdown) ││
│ │ └── ActionMenu (terminate, view details) ││
│ └─────────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────────┤
│ Layer 4: Detail Views │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ SessionDetailDrawer (drawer/modal) ││
│ │ ├── SessionOverview (metadata tab) ││
│ │ ├── UserSessionManager (users in container) ││
│ │ ├── HeartbeatTimeline (chart) ││
│ │ └── SessionMetadata (JSON viewer) ││
│ └─────────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────────┤
│ Layer 5: Admin & System Views │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ SystemAdminDashboard (platform-wide for AZ1.AI) ││
│ │ ├── TenantSessionOverview (cross-tenant analytics) ││
│ │ ├── LicenseUtilizationChart ││
│ │ └── SessionAlertsBanner ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
Technology Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Framework | React 18 | Existing codebase standard |
| Language | TypeScript (strict) | Type safety, maintainability |
| Styling | TailwindCSS | Existing design system, utility-first |
| State (Server) | React Query | Caching, polling, mutations |
| State (UI) | Zustand | Simple, lightweight global state |
| Charts | Recharts | Declarative, React-native |
| Real-time | Polling → WebSocket | Phase 1: 5s polling, Phase 2: WebSocket |
Component Inventory (28 Total)
Phase 1: Core Dashboard (P0) - Week 1
| # | Component | Priority | LOC Est. | Files |
|---|---|---|---|---|
| 1 | ContainerSessionDashboard | P0 | 200 | src/components/containerSessions/ContainerSessionDashboard.tsx |
| 2 | DashboardHeader | P0 | 150 | src/components/containerSessions/DashboardHeader.tsx |
| 3 | SessionMetrics | P0 | 250 | src/components/containerSessions/SessionMetrics.tsx |
| 4 | SessionStatusBadge | P0 | 50 | src/components/containerSessions/SessionStatusBadge.tsx |
| 5 | ContainerTypeIcon | P0 | 40 | src/components/containerSessions/ContainerTypeIcon.tsx |
| 6 | HeartbeatTimer | P0 | 80 | src/components/containerSessions/HeartbeatTimer.tsx |
| 7 | SessionErrorBoundary | P0 | 100 | src/components/containerSessions/SessionErrorBoundary.tsx |
Phase 2: Filtering & Lists (P1) - Week 2
| # | Component | Priority | LOC Est. | Files |
|---|---|---|---|---|
| 8 | SessionFilters | P1 | 200 | src/components/containerSessions/SessionFilters.tsx |
| 9 | TenantFilter | P1 | 80 | src/components/containerSessions/filters/TenantFilter.tsx |
| 10 | ContainerTypeFilter | P1 | 60 | src/components/containerSessions/filters/ContainerTypeFilter.tsx |
| 11 | StatusFilter | P1 | 60 | src/components/containerSessions/filters/StatusFilter.tsx |
| 12 | UserCountIndicator | P1 | 50 | src/components/containerSessions/UserCountIndicator.tsx |
| 13 | ActionMenu | P1 | 100 | src/components/containerSessions/ActionMenu.tsx |
| 14 | EmptyState | P1 | 60 | src/components/containerSessions/EmptyState.tsx |
| 15 | NetworkErrorAlert | P1 | 80 | src/components/containerSessions/NetworkErrorAlert.tsx |
Phase 3: Detail Views (P1) - Week 2-3
| # | Component | Priority | LOC Est. | Files |
|---|---|---|---|---|
| 16 | SessionDetailDrawer | P1 | 300 | src/components/containerSessions/SessionDetailDrawer.tsx |
| 17 | SessionOverview | P1 | 150 | src/components/containerSessions/SessionOverview.tsx |
| 18 | HeartbeatTimeline | P2 | 200 | src/components/containerSessions/HeartbeatTimeline.tsx |
| 19 | SessionMetadataViewer | P2 | 100 | src/components/containerSessions/SessionMetadataViewer.tsx |
Phase 4: Admin Features (P1) - Week 3
| # | Component | Priority | LOC Est. | Files |
|---|---|---|---|---|
| 20 | SystemAdminDashboard | P1 | 300 | src/components/containerSessions/admin/SystemAdminDashboard.tsx |
| 21 | TenantSessionOverview | P1 | 250 | src/components/containerSessions/admin/TenantSessionOverview.tsx |
| 22 | LicenseUtilizationChart | P2 | 200 | src/components/containerSessions/admin/LicenseUtilizationChart.tsx |
| 23 | SessionAlertsBanner | P2 | 100 | src/components/containerSessions/admin/SessionAlertsBanner.tsx |
Hooks & State (Supporting)
| # | Component | Priority | LOC Est. | Files |
|---|---|---|---|---|
| 24 | useContainerSessions | P0 | 150 | src/hooks/useContainerSessions.ts ✅ COMPLETE |
| 25 | useDashboardStore | P1 | 150 | src/stores/dashboardStore.ts |
Already Implemented (B.4.5.2-B.4.5.6)
| # | Component | Status | Files |
|---|---|---|---|
| 26 | ContainerSessionList | ✅ COMPLETE | src/components/containerSessions/ContainerSessionList.tsx |
| 27 | ContainerSessionCard | ✅ COMPLETE | src/components/containerSessions/ContainerSessionCard.tsx |
| 28 | UserSessionManager | ✅ COMPLETE | src/components/containerSessions/UserSessionManager.tsx |
State Management Architecture
// AuthContext - User role and tenant context
interface AuthContextValue {
user: User;
role: UserRole; // system_admin | tenant_admin | team_manager | user
tenant: Tenant | null;
isSystemAdmin: boolean;
}
// Zustand Dashboard Store - UI state
interface DashboardState {
view: 'grid' | 'list';
filters: SessionFilters;
selectedSessionId: string | null;
isDetailDrawerOpen: boolean;
page: number;
sortBy: string;
sortOrder: 'asc' | 'desc';
}
// React Query - Server state
// useContainerSessions(filters) - List with 5s polling
// useContainerSession(id) - Single session with 5s polling
// useContainerSessionStats() - Dashboard stats with 30s polling
// useTerminateSession() - Mutation with optimistic update
// useKickUser() - Mutation with optimistic update
Real-time Update Strategy
Phase 1 (Immediate): Polling
- Session list: 5-second intervals
- Session detail: 5-second intervals
- Dashboard stats: 30-second intervals
- Heartbeat timer: 1-second local countdown
Phase 2 (Future): WebSocket Upgrade
- Django Channels backend
- WebSocket events: session.created, session.updated, session.released, user.joined, user.left
- Fallback to polling if WebSocket fails
Role-Based UI Rendering
| UI Element | System Admin | Tenant Admin | Team Manager | User |
|---|---|---|---|---|
| TenantFilter | ✅ | ❌ | ❌ | ❌ |
| All Sessions | ✅ | Tenant only | Team only | Own only |
| Terminate Action | ✅ | ✅ | ✅ | ❌ |
| Kick User Action | ✅ | ✅ | ✅ | ❌ |
| SystemAdminDashboard | ✅ | ❌ | ❌ | ❌ |
| License Utilization | ✅ | ✅ | ❌ | ❌ |
API Integration
All components use the Container Session API (ADR-055):
| Endpoint | Method | Component Usage |
|---|---|---|
/api/v1/sessions/ | GET | ContainerSessionList |
/api/v1/sessions/{id}/ | GET | SessionDetailDrawer |
/api/v1/sessions/stats/ | GET | SessionMetrics |
/api/v1/sessions/validate/ | POST | (Client SDK only) |
/api/v1/sessions/heartbeat/ | POST | (Client SDK only) |
/api/v1/sessions/release/ | POST | (Client SDK only) |
/api/v1/sessions/{id}/ | DELETE | ActionMenu (terminate) |
/api/v1/sessions/{id}/users/{user_id}/ | DELETE | UserSessionManager (kick) |
Consequences
Positive
- Complete Visibility - Admins can monitor all container sessions
- Real-time Monitoring - Live heartbeat status and countdown timers
- Role-appropriate Access - Each role sees only what they need
- Extensible Architecture - Easy to add new container types or views
- Production-ready - Error handling, loading states, accessibility
Negative
- Complexity - 28 components requires significant development effort (~3 weeks)
- Polling Overhead - 5-second polling may strain API at scale (mitigated by WebSocket upgrade path)
- State Synchronization - Multiple data sources (React Query + Zustand) require careful coordination
Risks
| Risk | Probability | Impact | Mitigation |
|---|---|---|---|
| Performance degradation with 500+ sessions | Medium | High | Implement virtualized lists, pagination |
| WebSocket upgrade complexity | Low | Medium | Robust polling fallback |
| Role-based logic bugs | Medium | Medium | Comprehensive E2E tests per role |
Implementation Roadmap
Week 1: Core Dashboard
- B.4.5.7: ContainerSessionDashboard
- B.4.5.8: DashboardHeader
- B.4.5.9: SessionMetrics (3 cards)
- B.4.5.10-12: Supporting components (StatusBadge, TypeIcon, Timer)
- B.4.5.13: SessionErrorBoundary
Week 2: Filters & Detail Views
- B.4.5.14: SessionFilters (combined)
- B.4.5.15-17: Individual filters (Tenant, Type, Status)
- B.4.5.18: SessionDetailDrawer
- B.4.5.19: SessionOverview
- B.4.5.20: useDashboardStore (Zustand)
Week 3: Admin & Polish
- B.4.5.21: SystemAdminDashboard
- B.4.5.22: TenantSessionOverview
- B.4.5.23: LicenseUtilizationChart
- B.4.5.24: SessionAlertsBanner
- B.4.5.25-27: Error states (Empty, Network, Boundary)
- B.4.5.28: HeartbeatTimeline chart
Compliance
- ADR-054: Task IDs follow Track.Section.Task format (B.4.5.x)
- ADR-055: Uses Container Session API endpoints
- CODITECT-STANDARD: TailwindCSS, TypeScript strict mode, React Query
References
Revision History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0.0 | 2026-01-05 | Claude (ADR-056) | Initial architecture decision |