5.9 KiB
React Dashboard Migration
Outcome
Replace the inline dashboard in public/index.html with a typed, accessible React
application without disrupting the bot worker or its Express API. At cutover,
Express will remain the authenticated API, SSE, private-media, and static-asset
host.
Current status
The React application and production build are present. The current routes are
Overview, Conversations (including route-addressable detail), Media,
Performance, Operations, and a Settings placeholder. Shared HTTP and realtime
schemas live in packages/api-contracts, and /api/events/stream supplies the
typed SSE transport.
npm run build compiles the server and writes the Vite application to
apps/dashboard/dist. src/web/dashboard-assets.ts can select that build and
fall back to public/index.html, but the checked-in Express server has not yet
completed that authenticated static-hosting cutover. Until it does, the legacy
page remains the served fallback and the dashboard must not be exposed to an
untrusted network.
The migration is complete only when the legacy dashboard can be removed without losing a documented capability and the full test, type-check, production-build, browser-acceptance, authentication, private-media, and reconnect gates pass.
Architecture
apps/dashboard: React, TypeScript, Vite, React Router, and TanStack Query.packages/api-contracts: shared Zod schemas for HTTP and realtime transport.src/web: existing Express host, gradually adapted to the shared contracts.- Backend service and database types stay private to the backend. Transport contracts must not expose database rows as an accidental public API.
- Server state lives in TanStack Query. Route, filter, and selection state lives in URLs. Local component state is reserved for ephemeral presentation state.
- Do not add a global client-state library until a demonstrated cross-feature state requirement cannot be met by those boundaries.
Navigation
The application shell provides a responsive sidebar or drawer with these routes:
| Route | Responsibility |
|---|---|
/ |
Health, metrics, timers, limits, and important recent activity |
/conversations |
Searchable conversation inbox |
/conversations/:id |
Conversation history, context, suggestions, and sending |
/media |
Private blurred-by-default gallery and sync controls |
/performance |
Trends, strategies, subreddits, and successful examples |
/operations |
Domain activity, background jobs, and diagnostics |
/settings |
Placeholder for runtime and access settings |
Overview surfaces bot state, dry-run state, cooldowns, timers, and the guarded start/stop control. Operations owns connection health, sync state, activity, and the bounded technical console.
Realtime model
HTTP snapshots are authoritative. A typed SSE event stream updates or invalidates
the relevant query cache. Every event has an ID, type, occurrence timestamp, and
validated payload. The stream supports bounded replay and Last-Event-ID resume.
Relevant events invalidate TanStack Query keys so the next HTTP read remains
authoritative. Connection loss marks cached state stale while EventSource
reconnects.
Required event families are bot status, message sent or failed, conversation updated, reply received, sync lifecycle and progress, rate-limit state, Reddit session state, and technical logs.
Important domain events appear in a readable activity timeline. Raw logs remain in Operations. Toasts are reserved for user-triggered outcomes requiring timely feedback. Realtime disconnection never blanks cached content; it marks it stale and falls back to visibility-aware polling where necessary.
Feature slices
- Capture and validate existing HTTP contracts and critical legacy journeys.
- Add the frontend build, application shell, authentication handling, routing, error boundaries, design tokens, query client, and test harness.
- Deliver Overview as the first production vertical slice.
- Add the typed realtime event core, SSE transport, reconnection, and query invalidation.
- Migrate Conversations, including explicit suggestion and send mutations.
- Migrate Media, preserving authentication and blur-by-default privacy.
- Migrate Performance with sample sizes and low-confidence warnings.
- Migrate Operations, access/login, and route-backed validated Settings.
- Run feature parity and security audits, switch Express to the Vite output, remove the legacy HTML, and update operator and deployment documentation.
Each slice must be usable independently and must not require a flag-day rewrite. The legacy dashboard remains available until replacement behavior is proven.
Engineering gates
- New behavior follows red-green-refactor with Vitest.
- Component tests use Testing Library and test observable behavior.
- HTTP tests use a controlled boundary such as MSW rather than mocking
fetchthroughout feature tests. - Contract tests prove representative current responses parse and malformed payloads fail.
- Realtime tests cover ordering, replay, duplicate handling, reconnection, and cache repair.
- Browser tests cover authentication, start/stop, conversation inspection, sending confirmation, sync, settings persistence, and private media.
- Interactive controls are keyboard accessible and restore focus after dialogs.
- No production frontend or backend code uses
anyorconsole.log. - Mutations display the confirmed server outcome and do not rely on optimistic success for operational controls.
- Production builds are deterministic. Serving only generated frontend assets plus authenticated private content remains a cutover gate.
Cutover evidence
Before deleting public/index.html, record evidence for every legacy capability,
all API and SSE authentication paths, private media access, container restart and
SSE recovery, responsive navigation, frontend type-check/build, backend build,
the complete Vitest suite, and the critical Playwright journeys.