add design spec for issue #1 fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
168
docs/superpowers/specs/2026-03-12-issue1-fixes-design.md
Normal file
168
docs/superpowers/specs/2026-03-12-issue1-fixes-design.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# Issue #1 Fixes — Design Spec
|
||||
|
||||
**Goal:** Address all items in Gitea issue #1 — rework predictions to use full ESC entries with tap-to-assign UI, remove Dish of the Nation, rename acts, add player submission indicators, and make lobby code copyable.
|
||||
|
||||
**Source:** https://git.felixfoertsch.de/felixfoertsch/esc/issues/1
|
||||
|
||||
---
|
||||
|
||||
## 1. Entry Data Model
|
||||
|
||||
Replace the current country-only lineup with full ESC entries:
|
||||
|
||||
```ts
|
||||
type Entry = {
|
||||
country: { code: string; name: string; flag: string }
|
||||
artist: string
|
||||
song: string
|
||||
}
|
||||
|
||||
type Lineup = {
|
||||
year: number
|
||||
entries: Entry[]
|
||||
}
|
||||
```
|
||||
|
||||
The data file changes from `esc-2026.json` to `esc-2025.json` using real ESC 2025 entries for testing. Each entry includes the flag emoji in the data file.
|
||||
|
||||
Display format everywhere: `🇩🇪 Abor — Süden`
|
||||
|
||||
## 2. Prediction Model
|
||||
|
||||
Replace the current `{ predictedWinner, top3[], nulPointsPick }` with 4 ordered picks:
|
||||
|
||||
```ts
|
||||
type Prediction = {
|
||||
playerId: string
|
||||
first: string // country code
|
||||
second: string // country code
|
||||
third: string // country code
|
||||
last: string // country code
|
||||
}
|
||||
```
|
||||
|
||||
**Validation:** All 4 picks must be distinct country codes from the lineup.
|
||||
|
||||
**Scoring model** (for future implementation):
|
||||
- Any of 1st/2nd/3rd picks landing in the actual top 3 → points
|
||||
- 1st pick matching actual winner → bonus points
|
||||
- Last pick matching actual last place → bonus points
|
||||
|
||||
## 3. Prediction UI — Tap-to-Assign
|
||||
|
||||
**Top section — 4 slot cards:**
|
||||
- "1st Place", "2nd Place", "3rd Place", "Last Place"
|
||||
- Empty slots show placeholder text
|
||||
- Filled slots show the entry (flag + artist + song) with a tap-to-remove action
|
||||
|
||||
**Bottom section — scrollable entry list:**
|
||||
- All entries from the lineup
|
||||
- Already-assigned entries are dimmed/disabled
|
||||
- Tapping an unassigned entry shows a small picker with only unfilled slots (1st/2nd/3rd/Last)
|
||||
- After selecting a slot, the entry fills that slot and dims in the list
|
||||
|
||||
**Submit button:** Appears when all 4 slots are filled. After submission or when predictions are locked, the UI becomes read-only showing assigned entries in their slots.
|
||||
|
||||
**Locked state:** When advancing past Pre-Show, predictions lock. The form shows the player's submitted picks (or "Not submitted" if they missed the window).
|
||||
|
||||
## 4. Remove Dish of the Nation
|
||||
|
||||
Strip the entire Dish of the Nation feature:
|
||||
|
||||
**Server:**
|
||||
- Remove dish WS message handlers from `handler.ts`
|
||||
- Remove dish methods from `GameManager`
|
||||
- Remove dish persistence from `GameService`
|
||||
- Remove dish DB tables (`dishes`, `dish_guesses`) from schema
|
||||
|
||||
**Client:**
|
||||
- Delete `dish-list.tsx`, `dish-host.tsx`, `dish-results.tsx`
|
||||
- Remove dish state slices and actions from `room-store.ts`
|
||||
- Remove dish WS message handlers from `use-websocket.ts`
|
||||
- Remove dish UI from routes (`play`, `host`, `display`)
|
||||
|
||||
**Shared:**
|
||||
- Remove dish schemas from `game-types.ts`
|
||||
- Remove dish WS message types from `ws-messages.ts`
|
||||
|
||||
## 5. Player List — Prediction Checkmark
|
||||
|
||||
Add prediction submission status to the player state broadcast:
|
||||
|
||||
- Server: When building room state or game state, include `hasSubmittedPrediction: boolean` per player by checking if a prediction exists in the GameManager
|
||||
- Client: Player list component renders a checkmark icon (✓) next to player names that have submitted predictions
|
||||
- Visible on all views (play, host, display)
|
||||
|
||||
## 6. Acts Naming
|
||||
|
||||
Rename internal act identifiers and add display names:
|
||||
|
||||
```ts
|
||||
const ACTS = ["lobby", "pre-show", "live-event", "scoring", "ended"] as const
|
||||
```
|
||||
|
||||
| Internal ID | Display Name | Timing Intent |
|
||||
|----------------|---------------|----------------------------------|
|
||||
| `lobby` | Lobby | Waiting room, players join |
|
||||
| `pre-show` | Pre-Show | Before broadcast, predictions |
|
||||
| `live-event` | Live Event | During broadcast |
|
||||
| `scoring` | Scoring | After results, leaderboard |
|
||||
| `ended` | Ended | Party over |
|
||||
|
||||
Host control buttons: "Start Pre-Show", "Start Live Event", "Start Scoring", "End Party".
|
||||
|
||||
Predictions lock when advancing from Pre-Show to Live Event (previously act1 → act2).
|
||||
|
||||
## 7. Lobby Code — Copy to Clipboard
|
||||
|
||||
On the display view (and anywhere the room code is shown prominently):
|
||||
|
||||
- Wrap the room code in a tappable/clickable element
|
||||
- On click: `navigator.clipboard.writeText(roomCode)`
|
||||
- Show brief "Copied!" feedback (tooltip or temporary text swap)
|
||||
- Style to indicate interactivity (cursor pointer, subtle hover state)
|
||||
|
||||
## Data Flow Changes
|
||||
|
||||
### Prediction Flow (updated)
|
||||
1. Client taps entry → selects slot → slot fills in UI
|
||||
2. Client fills all 4 slots → submits `submit_prediction` with `{ first, second, third, last }`
|
||||
3. Server validates: all 4 distinct, all valid country codes
|
||||
4. Server stores in GameManager, persists to DB
|
||||
5. Server broadcasts updated game state (includes `hasSubmittedPrediction` per player)
|
||||
6. All clients update player list checkmarks
|
||||
|
||||
### Act Progression (updated names)
|
||||
```
|
||||
lobby → pre-show → live-event → scoring → ended
|
||||
```
|
||||
Predictions lock on `pre-show → live-event` transition.
|
||||
|
||||
## Files Affected
|
||||
|
||||
### Modified
|
||||
- `packages/shared/src/game-types.ts` — entry type, prediction model
|
||||
- `packages/shared/src/ws-messages.ts` — remove dish messages, update prediction message
|
||||
- `packages/shared/src/constants.ts` — act names
|
||||
- `packages/server/data/` — replace `esc-2026.json` with `esc-2025.json`
|
||||
- `packages/server/src/games/game-manager.ts` — remove dish logic, update prediction logic
|
||||
- `packages/server/src/games/game-service.ts` — remove dish persistence, update prediction columns
|
||||
- `packages/server/src/rooms/room-manager.ts` — act name references
|
||||
- `packages/server/src/ws/handler.ts` — remove dish handlers, update prediction handler
|
||||
- `packages/server/src/db/schema.ts` — remove dish tables, update prediction columns
|
||||
- `packages/server/tests/game-manager.test.ts` — rewrite for new model
|
||||
- `packages/server/tests/ws-handler.test.ts` — update for changed messages
|
||||
- `packages/client/src/stores/room-store.ts` — remove dish state, update game state shape
|
||||
- `packages/client/src/hooks/use-websocket.ts` — remove dish handlers
|
||||
- `packages/client/src/components/predictions-form.tsx` — rewrite as tap-to-assign
|
||||
- `packages/client/src/routes/play.$roomCode.tsx` — remove dish UI
|
||||
- `packages/client/src/routes/host.$roomCode.tsx` — remove dish UI
|
||||
- `packages/client/src/routes/display.$roomCode.tsx` — remove dish UI, add copy-to-clipboard
|
||||
|
||||
### Deleted
|
||||
- `packages/client/src/components/dish-list.tsx`
|
||||
- `packages/client/src/components/dish-host.tsx`
|
||||
- `packages/client/src/components/dish-results.tsx`
|
||||
|
||||
### Created
|
||||
- `packages/server/data/esc-2025.json` — full ESC 2025 entry data
|
||||
Reference in New Issue
Block a user