scoreboardtools/apps/web/src/pages/Control.tsx
Ashraf Shafiq 9169bea79f Scaffold real-time SSBU scoreboard PWA
pnpm monorepo with three workspaces:
- @sbt/shared: zod ScoreboardState + WebSocket protocol (single source of truth)
- @sbt/server: Fastify REST + raw ws WebSocket + Drizzle/Postgres, run via tsx
- @sbt/web: React + Vite + Tailwind installable PWA

Real-time core: the WebSocket server holds authoritative per-board state in
memory, broadcasts to all clients (editors + OBS overlays) instantly, and
debounces Postgres saves (~750ms). One useScoreboardSync hook powers the editor,
the no-login co-edit control page, and the read-only OBS overlay.

Includes email+password auth (JWT cookie), scoreboard CRUD, logo upload,
customizable scorebug (characters/stocks/score/subtitles/callout/side-swap/theme),
Docker Compose + Caddy/nginx deploy configs, and docs/PLAN.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 00:09:27 -04:00

26 lines
978 B
TypeScript

import { useParams } from 'react-router-dom';
import { useScoreboardSync } from '../hooks/useScoreboardSync';
import { Workspace } from '../components/Workspace';
/** Public co-scorekeeping page. No login — access is via the unguessable control token. */
export function Control() {
const { token = '' } = useParams();
const { state, sendUpdate, connected } = useScoreboardSync(`control=${token}`, 'editor');
return (
<div className="min-h-screen bg-ink text-white">
<header className="border-b border-edge px-6 py-4">
<h1 className="text-lg font-bold">Scorekeeper</h1>
<p className="text-xs text-slate-500">Shared control your changes go live instantly.</p>
</header>
<main className="mx-auto max-w-5xl p-6">
{state ? (
<Workspace state={state} onChange={sendUpdate} connected={connected} />
) : (
<p className="text-slate-400">Connecting</p>
)}
</main>
</div>
);
}