This page couldn't load
++ The server or the connection had a problem. Nothing you entered before this was lost. Check the + Wi-Fi and try again. +
+ +diff --git a/README.md b/README.md index 9b96876..4184028 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,36 @@ an admin control room, a projector scoreboard, and per-game judge links. - Open `docs/KICKOFF_PROMPT.md`, paste its contents into Claude Code. - Let it go phase by phase. After each phase: run it, click around, commit, then say "next phase". -## Tips +## Commands -- Keep `docs/` as the source of truth. If you change your mind on a feature, edit the doc first, then tell Claude Code "I updated PRD.md section X, implement it". -- Test the scoreboard on the actual projector PC + one judge on a phone before game night. Wi-Fi at church is the #1 thing that'll bite you. +| Command | What it does | +|---|---| +| `npm run dev` | Run locally at http://localhost:3000 | +| `npm run test` / `npm run test:coverage` | Unit tests (scoring engine must stay at 100%) | +| `npm run lint` / `npm run build` | Lint / production build | +| `npm run check:rls` | Proves the anon key cannot read or write any table | +| `npm run gen:types` | Regenerate `src/lib/database.types.ts` after a migration | + +## Deploying to Vercel + +1. Push this repo to GitHub, then in Vercel choose **Add New → Project** and import it. +2. Add these environment variables in Vercel (Production): `NEXT_PUBLIC_SUPABASE_URL`, + `NEXT_PUBLIC_SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`, `ADMIN_EMAILS`, + `NEXT_PUBLIC_APP_NAME`, and `NEXT_PUBLIC_SITE_URL` set to the site's address + (e.g. `https://your-project.vercel.app`, no trailing slash). The build fails with a clear + message if any is missing. +3. Deploy. `vercel.json` runs the server functions in Dublin (`dub1`), next to a West EU + Supabase project; change it if your database is elsewhere. +4. In Supabase → Authentication → URL Configuration, set **Site URL** to the Vercel address and + add `https://your-project.vercel.app/auth/confirm` to **Redirect URLs** (keep the localhost + one for development). +5. If you change `NEXT_PUBLIC_SITE_URL` later, redeploy: share links and QR codes are built from it. + +## Before game night + +- Free Supabase projects pause after about a week without use. Open the Supabase dashboard a + day before and click **Restore** if it is paused. +- Sign in as host the day before; the magic-link email is limited to a few per hour. +- Test the scoreboard on the actual projector PC and one judge link on a phone, on the venue's + Wi-Fi. The scoreboard also polls every 10 seconds if live updates are blocked. +- Keep `docs/` as the source of truth: change the doc first, then the code. diff --git a/docs/BUILD_PLAN.md b/docs/BUILD_PLAN.md index 4fb6681..f2c0937 100644 --- a/docs/BUILD_PLAN.md +++ b/docs/BUILD_PLAN.md @@ -5,7 +5,7 @@ human a short "how to test this manually" checklist. Don't start the next phase ## Phase 0 — Scaffold - Next.js + TS strict + Tailwind + ESLint + Vitest. `src/` layout per ARCHITECTURE.md. -- Fonts: Big Shoulders Display + Atkinson Hyperlegible via `next/font/google`. Tokens from UI.md as CSS variables + Tailwind theme. +- Fonts: Big Shoulders (Google's new name for Big Shoulders Display) + Atkinson Hyperlegible via `next/font/google`. Tokens from UI.md as CSS variables + Tailwind theme. - Supabase clients (`browser`, `server`, `admin` with `server-only`). - `.env.local` loading; fail fast with a clear error if vars are missing. **Done when:** `npm run dev` shows a placeholder home; `npm run test` runs a dummy test. diff --git a/docs/UI.md b/docs/UI.md index d9688d1..1917d7a 100644 --- a/docs/UI.md +++ b/docs/UI.md @@ -12,11 +12,12 @@ Spend the boldness in ONE place: the scoreboard's rank-change moment. Admin + ju | Chalk | `#F5F7FA` | Admin/judge background | | Ink | `#14213D` | Primary text on light | | Signal | `#2EC4B6` | Saved/connected state | -| Alert | `#E63946` | Errors, penalties, hidden state | +| Alert | `#E63946` | Penalty/hidden accents, borders, status dots | +| Alert strong | `#C1121F` | Error text on light backgrounds, and red fills under white text (Alert itself is only 4.0:1 on white) | Team colours are user-chosen; pick text colour per team by contrast (white or Ink). Type: -- **Big Shoulders Display** (Google Fonts) — scoreboard numbers, team names, game titles. Tabular numbers. +- **Big Shoulders** (Google Fonts; formerly "Big Shoulders Display") — scoreboard numbers, team names, game titles. Tabular numbers. - **Atkinson Hyperlegible** — all body/UI text (readable on phones + projector). Sentence case everywhere. No all-caps labels. diff --git a/src/app/admin/events/[eventId]/page.tsx b/src/app/admin/events/[eventId]/page.tsx index f0df0c0..b33e03d 100644 --- a/src/app/admin/events/[eventId]/page.tsx +++ b/src/app/admin/events/[eventId]/page.tsx @@ -3,6 +3,7 @@ import { notFound } from "next/navigation"; import { EventDetailsForm } from "@/components/admin/event-details-form"; import { GamesEditor } from "@/components/admin/games-editor"; +import { SharePanel } from "@/components/admin/share-panel"; import { TeamsEditor } from "@/components/admin/teams-editor"; import { buttonClass } from "@/components/ui/button"; import { requireAdmin } from "@/lib/auth"; @@ -21,7 +22,7 @@ export default async function EventSetupPage(props: PageProps<"/admin/events/[ev return (
+ The server or the connection had a problem. Nothing you entered before this was lost. Check the + Wi-Fi and try again. +
+ +{state.status === "sent" && {state.message}} - {error && {error}} + {error && {error}}
); diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx new file mode 100644 index 0000000..4909cc6 --- /dev/null +++ b/src/app/not-found.tsx @@ -0,0 +1,17 @@ +import Link from "next/link"; + +import { buttonClass } from "@/components/ui/button"; + +export default function NotFound() { + return ( ++ That link doesn't lead anywhere. If the host sent it to you, ask them for a new one. +
+ + Go to the start page + +Scaffold is running. Admin, scoreboard and judge pages come next.
++ Live scoring for game nights: a control room for the host, a scoreboard for the projector and + links for judges' phones. +
Judging or watching the scoreboard? Use the link or QR code the host gave you.
+
{children}
); diff --git a/src/components/admin/judged-panel.tsx b/src/components/admin/judged-panel.tsx index 693e1ea..0131a63 100644 --- a/src/components/admin/judged-panel.tsx +++ b/src/components/admin/judged-panel.tsx @@ -93,7 +93,7 @@ export function JudgedPanel({ ✓ all scored ) : scored === 0 ? ( - + ✗ nothing scored ) : ( diff --git a/src/components/admin/judges-editor.tsx b/src/components/admin/judges-editor.tsx index 6b581ca..1016907 100644 --- a/src/components/admin/judges-editor.tsx +++ b/src/components/admin/judges-editor.tsx @@ -30,7 +30,7 @@ export function JudgesEditor({ eventId, game }: { eventId: string; game: GameWit
+ These links point at {site}, which only works on this computer. Phones can open
+ them once the site is deployed and NEXT_PUBLIC_SITE_URL is set to its address.
+
Scoreboard (projector)
+No judged games yet. Set a game to “Judged” to give judges their own links.
+ ) : ( + judgedGames.map((game) => { + const activeJudges = game.judges.filter((judge) => !judge.revoked); + return ( +No judges yet. Open this game below to add judges or turn on its open link.
+ )} +Couldn't copy automatically. Select the text below.
+{text}
+ +
{error}
diff --git a/src/components/scoreboard/scoreboard.tsx b/src/components/scoreboard/scoreboard.tsx index 25ecdb4..4fc11ba 100644 --- a/src/components/scoreboard/scoreboard.tsx +++ b/src/components/scoreboard/scoreboard.tsx @@ -143,7 +143,7 @@ export function Scoreboard({ token, initial }: { token: string; initial: Scorebo