Files
guru-connect/dashboard/README.md
Mike Swanson 43a9432b81
All checks were successful
Build and Test / Build Agent (Windows) (push) Successful in 6m56s
Build and Test / Build Server (Linux) (push) Successful in 10m15s
Build and Test / Security Audit (push) Successful in 4m12s
Build and Test / Build Summary (push) Successful in 10s
feat(dashboard): GuruConnect v2 operator console (pass 1)
React + Vite + TypeScript SPA: scaffold, operations-terminal design
system, Bearer-token auth, and the Machines view.

- Design system: OKLCH-tinted dark theme (ink-slate + signal-cyan),
  Hanken Grotesk + JetBrains Mono, status-color language
  (online/offline/granted/pending/denied/not_required), motion with
  prefers-reduced-motion honored.
- Auth: token in sessionStorage via ref (never React state), protected
  routes, 401 session teardown, admin-gated per-agent-key UI.
- Machines view: data table (sticky header, keyboard-activated rows,
  skeleton loading, actionable empty/error states), non-blocking detail
  drawer, delete confirm, admin key management with copy-once reveal.
- UI primitives: Modal (focus trap + inert + portal + dialogStack),
  Drawer, Table, Badge/StatusDot, toast, states.
- Typed API client normalizing the two error-envelope shapes.

Passed Code Review (no blockers), impeccable critique-and-polish, and
local gates (tsc/lint/build green). Dev-only Vite proxy to :3002.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 12:51:11 -07:00

4.8 KiB

GuruConnect Operator Dashboard (v2)

React + Vite + TypeScript SPA — the operator console for GuruConnect v2. A dark "operations terminal" UI for managing the remote-support fleet.

Pass 1 scope. This pass ships the scaffold, design system, app shell, auth, the typed API client, and the Machines view. Sessions, Codes, and Users are nav stubs only (disabled in the sidebar) and arrive in later passes.

Stack

  • React 18 + React Router 6 (client-side routing)
  • Vite 5 (dev server + build)
  • TypeScript (strict)
  • @tanstack/react-query (server-state, polling, cache invalidation)
  • @fontsource — Hanken Grotesk (UI) + JetBrains Mono (technical data)

No component/icon libraries — primitives and icons are hand-built to keep the console aesthetic and the bundle lean.

Scripts

npm install
npm run dev        # Vite dev server (proxies /api + /ws to the local GC server)
npm run build      # tsc -b && vite build  ->  dist/
npm run preview     # serve the production build locally
npm run typecheck  # tsc --noEmit
npm run lint       # eslint

Project layout

src/
  api/            Typed API client + response interfaces (source of truth: server/src/api/*.rs)
    client.ts       fetch wrapper: base URL, bearer token, dual error-envelope normalization
    types.ts        TS mirrors of the Rust response structs
    auth.ts         login / me / logout
    machines.ts     list / get / history / delete + admin key endpoints
    stubs.ts        sessions / codes / users — scaffolds for later passes
  auth/           AuthProvider (token in memory + sessionStorage), context, ProtectedRoute
  components/
    ui/             Reusable primitives: Button, Badge/StatusDot, Table, Panel,
                    Modal, ConfirmDialog, Input/Field, Spinner, States, Toast
    layout/         AppShell, Sidebar, Topbar, PageHeader, inline SVG icons
  features/
    auth/           LoginPage
    machines/       MachinesPage + detail / delete / admin-keys modals + hooks
  lib/            time formatting, clipboard, relay-status probe
  styles/         tokens.css (design tokens)

Design system — "operations terminal"

Dark control-room console. Tokens live in src/styles/tokens.css; primitive styles in src/components/ui/*.css.

  • Surfaces: --bg #0b0f14, --panel #141b22, --panel-2 #0e1419
  • Accent (signal cyan): --accent #22d3bf — primary actions + live state
  • Status language (dot + label, used everywhere): ok/online --ok, pending --warn (soft pulse), denied/offline/error --bad, neutral --neutral. Mapping centralised in components/ui/status.ts.
  • Type: Hanken Grotesk for UI; JetBrains Mono for all technical data (agent IDs, support codes, IPs, versions, timestamps, key fingerprints).
  • Motion (restrained): staggered row fade-in, the consent pulse, the live relay pip, hover transitions. All disabled under prefers-reduced-motion.

Auth

POST /api/auth/login{ token, user }. The token is held in an in-memory ref and mirrored to sessionStorage (never localStorage), so it clears when the tab closes. GET /api/auth/me restores the session on reload; POST /api/auth/logout revokes it server-side. The client attaches Authorization: Bearer <token> to every request and bounces to /login on any 401. Admin-only UI (per-agent key management) is gated on role === "admin".

The API uses two error envelopes — { error } and { detail, error_code, status_code }. api/client.ts extracts a message from whichever is present (and falls back to plain-text bodies that some routes return), so callers see one normalized ApiError.

Dev proxy

vite.config.ts proxies /api and /ws to the local GC server (http://localhost:3002). Run the Rust server locally, then npm run dev — same-origin requests reach the backend with no CORS setup.

To develop the UI against a remote backend instead, set VITE_API_URL (see .env.example).

Production serving — follow-up (NOT wired in this pass)

The build uses base: "./" so emitted assets use relative paths. Production serving means copying dist/ into the GC server's static directory and adding a catch-all route that returns index.html for non-API, non-asset paths (so deep links like /machines survive a hard reload under the BrowserRouter).

That Rust-side wiring is a deploy concern and is intentionally left for a later step:

  1. Copy dist/server/static/ (or serve dist/ directly).
  2. Add an Axum fallback route serving index.html for unmatched GET paths, after the /api/*, /ws/*, and static-asset routes.
  3. If the dashboard is mounted under a sub-path rather than the server root, switch Vite base to that path and pass the same basename to <BrowserRouter>.

No server/Rust changes were made in this pass.