A game can now carry a list of items the host puts on the scoreboard one at a time: a question (shown above the standings, with an optional picture and an answer to reveal), a sound (played through the scoreboard's speakers) or a video such as a karaoke track (takes over the screen). - Media is a YouTube link, an https link to a file, or an upload to the private game-media bucket. The scoreboard API signs a URL only for the item that is on screen. - What is on screen is state on the event, like the timer, so the ping still carries no data. An unrevealed answer and a sound's or video's name never reach the scoreboard payload. - Control room: click an item to show it, or "Random" to have one picked (prefers items not played yet). Show/hide answer, pause, take off screen; shortcuts Q / A / X. - Duplicating a game or event copies its items and files; deleting removes the files. Needs supabase/migrations/0006_screen_items.sql applied before deploying. database.types.ts was edited by hand to match; re-run gen:types after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
199 lines
13 KiB
Markdown
199 lines
13 KiB
Markdown
# PRD — Game Night Live
|
||
|
||
## 1. Goal
|
||
Run a youth game night (e.g. "Worship Game Night") from one admin tab while a projector tab shows a
|
||
live scoreboard, and judges score from their phones via links. Versatile enough for any game format.
|
||
|
||
## 2. Roles
|
||
| Role | Access | How they get in |
|
||
|---|---|---|
|
||
| **Admin** | Everything in the events they own or co-host | Logs in (magic link). Email must be in `ADMIN_EMAILS`. |
|
||
| **Scoreboard viewer** | Read-only scoreboard for ONE event | Public link `/s/{scoreboardToken}` |
|
||
| **Judge** | Score ONE game | Personal link `/j/{judgeToken}` OR open game link `/jo/{openToken}` (enters name first) |
|
||
| **Team** | Buzz in for ONE team | Team buzzer link `/b/{buzzerToken}`, one per team for the whole night |
|
||
|
||
## 3. Core concepts
|
||
- **Event** — one game night. Has teams, games, one scoreboard link, visibility state.
|
||
- **Team** — name + colour. Belongs to an event.
|
||
- **Game** — one activity. Ordered. Has a scoring mode:
|
||
- `judged` — judges submit scores via links; admin can see progress & override.
|
||
- `direct` — admin enters scores directly.
|
||
- **Criterion ("block")** — one thing being scored in a game. A game has 1..N criteria.
|
||
- e.g. Karaoke: Pitch, Energy, Lyrics accuracy, Stage presence, Crowd reaction (each 1–5).
|
||
- e.g. Guess the Lyric: one criterion "Correct answers" (number input, 0–20).
|
||
- Input type: `blocks` (tap a number in a row of buttons) or `number` (free numeric input / +/- stepper).
|
||
- Has min, max, step, weight, and a description (shown to whoever scores it).
|
||
- **Points mode** per game (how a game's result becomes leaderboard points):
|
||
- `raw` — game points = criteria total × multiplier.
|
||
- `placement` — teams ranked by criteria total, then awarded placement points (default 10/7/5/3).
|
||
- **Adjustment** — bonus or penalty points with a reason, optionally tied to a game.
|
||
- **Notes** — `admin_notes` (only admin sees) and `judge_notes` (judges see) per game.
|
||
|
||
## 4. Features
|
||
|
||
### 4.1 Admin — Setup (`/admin/events/[id]`)
|
||
- Create/edit/delete events; duplicate an event (copies teams, games, criteria — not scores).
|
||
- Teams: add, rename, recolour, reorder, delete.
|
||
- Games builder: add, reorder (drag or up/down), duplicate, delete.
|
||
- Title, description, scoring mode, points mode, multiplier, placement points.
|
||
- Criteria list: add/remove/reorder; label, description, input type, min/max/step, weight.
|
||
- Quick presets: "Karaoke (5 × 1–5)", "Single score (0–10)", "Points (number)", "Win/Lose (0–1)".
|
||
- Admin notes + judge notes (multiline).
|
||
- "Teams buzz in" checkbox: this game uses the team buzzers (see 4.7).
|
||
- Questions, sounds and videos to put on the scoreboard during the game (see 4.8).
|
||
- Judges (judged games only):
|
||
- Nominate judges by name → each gets a personal link (copy button + QR code).
|
||
- Toggle the open link on/off; anyone opening it types their name and becomes a judge.
|
||
- Revoke a judge (link stops working, their scores excluded).
|
||
- Share panel: scoreboard link + QR, all judge links per game, and one buzzer link + QR per team
|
||
(shown once any game uses buzzers).
|
||
- Co-hosts: the admin who created an event (its owner) shares it with other admins by email.
|
||
- The email must already be in `ADMIN_EMAILS`; it can be added before that admin's first login.
|
||
- A co-host sees the event in their list ("Shared with you") and can do everything in it —
|
||
setup, control room, scores — except delete it or change who it is shared with.
|
||
- The owner can remove a co-host at any time. Duplicating a shared event gives the co-host
|
||
their own copy, which they own.
|
||
|
||
### 4.2 Admin — Control room (`/admin/events/[id]/live`)
|
||
This is the screen used during the night. Must be fast and hard to mess up.
|
||
- Game switcher: Prev / Next buttons + list of all games with status (upcoming / live / done).
|
||
Setting a game as current marks it `live` and shows it on the scoreboard.
|
||
- Current game panel:
|
||
- Scoring grid: rows = teams, columns = criteria, using the same block/number inputs.
|
||
- Direct mode: admin types/taps here. Autosaves.
|
||
- Judged mode: shows per-team average per criterion, a judge-progress matrix
|
||
(✓ per judge per team), and per-judge breakdown on expand. Admin can override a cell
|
||
(override wins over judge average; clearly marked; can be cleared).
|
||
- Spotlight: pick "now performing" team — judges see it highlighted.
|
||
- Lock judging toggle (judges become read-only).
|
||
- Mark game done.
|
||
- Admin notes visible.
|
||
- Bonus/penalty: per team quick buttons (+1, +5, +10, −5) and custom amount + reason.
|
||
Recent adjustments list with undo.
|
||
- Scoreboard visibility: **Visible / Frozen / Hidden** (see 4.3). Big obvious toggle.
|
||
- "Open scoreboard" button (new tab) + "Reveal mode" button (see 4.3).
|
||
- Buzzers (buzzer games only): who buzzed, in order, with each team's gap behind the first;
|
||
"Got it" / "Wrong" for the team answering, "Undo" on a marked team, and "Next question".
|
||
- On the scoreboard (games with questions, sounds or videos): the game's list, a button per item
|
||
to show or play it, "Random" per kind, show/hide answer, pause/resume, take off screen (see 4.8).
|
||
- Keyboard shortcuts: `N` next game, `P` prev game, `H` cycle visibility, `T` start/pause timer,
|
||
`G` got it, `W` wrong, `B` next question (clears the buzzers), `Q` show the next question,
|
||
`A` show/hide the answer, `X` take it off screen, `?` help.
|
||
|
||
### 4.3 Scoreboard (`/s/[token]`)
|
||
- Projector-first, 16:9, readable from the back of a hall.
|
||
- Shows: event name, current game title, team standings (rank, name, colour, total points).
|
||
v1 shows totals only; per-game points columns are a later option.
|
||
- Updates live (< 1s) when admin changes anything. Rank changes animate.
|
||
- **Visible** — live totals.
|
||
- **Frozen** — shows totals as they were the moment it was frozen, with a subtle "scores frozen" note.
|
||
Server stores the snapshot; live scores are not sent. The current game title still updates live.
|
||
- **Hidden** — no scores sent at all. Shows team names (no order implied — alphabetical) and the current game, with a "scores are hidden" state.
|
||
- **Reveal mode** (admin-triggered): scoreboard reveals final standings last place → first,
|
||
one team per admin click ("Reveal next") — tied teams too, each showing the shared rank — then sets visibility to Visible.
|
||
- Auto-reconnects; also polls every 10s as a fallback. Shows a small offline dot if disconnected.
|
||
- Fullscreen button; hides cursor after inactivity.
|
||
|
||
### 4.4 Judge (`/j/[token]`)
|
||
- Mobile-first. Shows game title, judge notes, and their own name.
|
||
- One card per team (spotlighted team pinned on top + highlighted).
|
||
- Each card: one block row per criterion (e.g. 1 2 3 4 5 big tap targets) with the criterion
|
||
label + description; number criteria get a stepper.
|
||
- Autosaves on every tap; shows saved ✓ / saving / error per card.
|
||
- Judges see only their own scores — never other judges' or totals.
|
||
- If judging locked or game done: read-only with a message. If revoked: "This link is no longer active."
|
||
- Open link (`/jo/[token]`): enter name → creates judge → redirects to personal link and stores it
|
||
in localStorage so a refresh keeps them in.
|
||
|
||
### 4.5 Timer
|
||
One countdown timer per event, run from the control room; not tied to a game.
|
||
- Set a time (minutes + seconds, or presets 30s / 1 min / 2 min / 5 min). Setting a time resets the timer.
|
||
- Start, pause, resume, reset. Show or hide it on the scoreboard; starting it also shows it.
|
||
- The scoreboard shows it large above the standings. It is not a score, so it shows whether
|
||
scores are visible, frozen or hidden.
|
||
- The last 10 seconds are highlighted; at zero it shows "Time's up" until reset.
|
||
- The server's clock decides when it ends, so every screen agrees even if a device clock is wrong.
|
||
- Keyboard shortcut in the control room: `T` start/pause.
|
||
|
||
### 4.6 Sound effects (scoreboard only)
|
||
Played by the scoreboard page, so they come out of the projector PC's speakers.
|
||
- Reveal: a drum-roll, then a crash as each team appears; a fanfare for first place.
|
||
- Game transition: a chime when the current game changes.
|
||
- Timer: a tick for each of the last 10 seconds (sharper for the last 3) and a buzzer at zero.
|
||
- Browsers only allow sound after a click on the page, so the scoreboard has a "Turn sound on"
|
||
button. The choice is remembered, but after a reload one click anywhere is needed again.
|
||
- Buzzer: a two-note ding when a team starts answering, a rising arpeggio for "got it" and two
|
||
falling notes for "wrong".
|
||
- Sounds are synthesised in the browser (no audio files). With reduced motion the drum-roll is skipped.
|
||
|
||
### 4.7 Buzzers (`/b/[token]`)
|
||
For "first to answer" games. A game opts in with the "Teams buzz in" checkbox.
|
||
- Every team has one buzzer link for the whole night. Someone on the team opens it on a phone:
|
||
team name, the current game, and one big "Buzz" button.
|
||
- The button works only while the current game uses buzzers and isn't done. Otherwise the page
|
||
says there's no buzzer for this game, and wakes up by itself when one starts.
|
||
- The server decides the order, using the database's clock — never the phones' clocks.
|
||
- A team buzzes once per question; pressing again does nothing. Later teams can still buzz and
|
||
queue up behind the team answering.
|
||
- The team answering is the earliest buzz the host hasn't marked. The host marks it:
|
||
- **Got it** (`G`) — the question is decided and the buzzers close. In a direct game the team
|
||
gets +1 in the game's first criterion (so the point follows the game's multiplier and
|
||
placement rules like any other score). In a judged game nothing is added: the judges score.
|
||
- **Wrong** (`W`) — that team is out for this question and the turn passes to the next team
|
||
in the buzz order. With nobody waiting, the buzzers stay open for the other teams.
|
||
- **Undo** on a marked team removes the mark, and takes the point back if it was "got it".
|
||
- **Next question** (`B`) clears everything and opens the buzzers again.
|
||
- If the point can't be added (the score is at the criterion's maximum, or +1 doesn't fit its
|
||
step), the answer stays unmarked and the host sees why.
|
||
- The phone's screen turns the team's colour while it is that team's turn ("You buzzed first!
|
||
Answer now." / "Your turn! Answer now.") and when it got it. Otherwise it shows the team's
|
||
place and who is answering, "Not this time" after a wrong answer, or who got it.
|
||
- The control room lists the order with the gap behind the first buzz ("+0.42s") and each mark.
|
||
- The scoreboard shows "{Team} buzzed!" in the team's colour with the waiting teams after it,
|
||
"{Team} to answer" when the turn passes, and "{Team} got it!". It is not a score, so it shows
|
||
whether scores are visible, frozen or hidden (the point itself follows the usual visibility rules).
|
||
- The phone asks the browser to keep the screen on while the page is open.
|
||
|
||
### 4.8 Questions, sounds and videos on the scoreboard
|
||
A game can carry a list of things the host puts on the scoreboard, one at a time.
|
||
- Three kinds:
|
||
- **Question** — text, an optional picture and an optional answer. Shows above the standings.
|
||
- **Sound** — plays through the scoreboard's speakers; the screen only says "Listen…".
|
||
Optional answer (e.g. the song's name).
|
||
- **Video** — a karaoke track or a clip. Takes over the whole screen until taken off.
|
||
- Media is a link or an upload:
|
||
- a YouTube link (for a sound, only the audio is used), or an `https://` link to a file;
|
||
- or a file uploaded from the setup page: image for a question, audio for a sound, video for
|
||
a video, up to 50 MB each.
|
||
- Setup: per game, add / edit / reorder / delete items. Duplicating a game or an event copies
|
||
them, uploaded files included.
|
||
- Control room: the current game's list. Click an item to put it on screen; clicking the one
|
||
already showing starts it again. Only one item is on screen at a time, and switching game
|
||
clears it.
|
||
- **Random** (per kind, when there are at least two): picks one for the host — e.g. the next
|
||
karaoke song. It prefers items that haven't been shown yet and never repeats the one on
|
||
screen. Shown items get a ✓; "Clear the ✓ ticks" starts over.
|
||
- **Show answer** (`A`) reveals the answer under the question or sound; again to hide it.
|
||
- **Pause / Resume** for a sound or video. **Take off screen** (`X`) returns to the scoreboard.
|
||
- `Q` shows the next question in list order.
|
||
- Nothing here is a score, so it shows whether scores are visible, frozen or hidden.
|
||
- Secrets stay on the server: an answer is not sent to the scoreboard until it is revealed, and a
|
||
sound's or video's name is never sent (it is often the answer).
|
||
- Sound needs the same "Turn sound on" click as the sound effects (4.6). Until then a video
|
||
plays muted and the screen says the sound is off.
|
||
|
||
## 5. Non-functional
|
||
- Live latency target < 1s on normal Wi-Fi.
|
||
- Works with ~10 teams, ~20 games, ~10 judges per game without lag.
|
||
- No data loss on refresh; all state server-side.
|
||
- Accessible: visible focus, sufficient contrast, tap targets ≥ 44px.
|
||
|
||
## 6. Out of scope (v1)
|
||
- Individual contestants within teams (stretch: `participants` per team).
|
||
- Transferring an event to another owner, and read-only co-hosts.
|
||
- Per-game timers (stretch). The event countdown timer is in 4.5; team buzzers are in 4.7.
|
||
- Buzzers for individual contestants, a configurable points value per correct answer, and
|
||
penalties for a wrong answer (use the bonus/penalty panel).
|
||
- Multiple-choice questions, questions on the judges' or teams' phones, importing a list of
|
||
questions from a file, and seeking within a video from the control room.
|