felt/.planning/phases/01-tournament-engine/01-PLAN-M.md
Mikkel Georgsen 21ff95068e docs(01): create Phase 1 plans (A-N) with research and feedback
14 plans in 6 waves covering all 68 requirements for the Tournament
Engine phase. Includes research (go-libsql, NATS JetStream, Svelte 5
runes, ICM complexity), plan verification (2 iterations), and user
feedback (hand-for-hand UX, SEAT-06 reword, re-entry semantics,
integration test, DKK defaults, JWT 7-day expiry, clock tap safety).

Wave structure:
  1: A (scaffold), B (schema)
  2: C (auth/audit), D (clock), E (templates), J (frontend scaffold)
  3: F (financial), H (seating), M (layout shell)
  4: G (player management)
  5: I (tournament lifecycle)
  6: K (overview/financials), L (players), N (tables/more)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 02:58:22 +01:00

180 lines
8.3 KiB
Markdown

# Plan M: Layout Shell — Header, Tabs, FAB, Toast, Data Table
---
wave: 3
depends_on: [01-PLAN-J]
files_modified:
- frontend/src/routes/+layout.svelte
- frontend/src/lib/components/Header.svelte
- frontend/src/lib/components/BottomTabs.svelte
- frontend/src/lib/components/FAB.svelte
- frontend/src/lib/components/Toast.svelte
- frontend/src/lib/components/DataTable.svelte
autonomous: true
requirements: [UI-01, UI-02, UI-03, UI-04, UI-07, UI-08]
---
## Goal
The layout shell wraps all page content: a persistent clock header, mobile bottom tab bar (or desktop sidebar), floating action button (FAB) for quick actions, toast notification system, and a reusable data table component. Multi-tournament tab switching when 2+ tournaments are active.
## Context
- **SvelteKit scaffold** — Plan J (theme, WS client, API client, auth/tournament state stores)
- **Svelte 5 runes** for all reactivity
- **Mobile-first** with responsive desktop layout (sidebar instead of bottom tabs)
- **48px minimum touch targets** — poker room environment
- See 01-RESEARCH.md: Pattern 3 (SvelteKit SPA)
## User Decisions (from CONTEXT.md)
- **Mobile-first bottom tab bar** — Overview, Players, Tables, Financials, More
- **FAB for quick actions** — Bust, Buy In, Rebuy, Add-On, Pause/Resume
- **Persistent header** showing clock, level, blinds, player count
- **Desktop/laptop sidebar** with wider content area
- **Toast notifications** (success, info, warning, error) with auto-dismiss
- **Multi-tournament switching** — tabs at top (phone)
## Tasks
<task id="M1" title="Implement layout shell: header, bottom tabs, FAB, toast, data table">
**1. Root Layout** (`frontend/src/routes/+layout.svelte`):
- Auth guard: if not authenticated, redirect to /login
- Structure:
```
┌─────────────────────────┐
│ PersistentHeader │ ← Fixed top, always visible
├─────────────────────────┤
│ [Tournament Tabs] │ ← Multi-tournament selector (when 2+ active)
├─────────────────────────┤
│ │
│ <slot /> │ ← Page content (scrollable)
│ │
├─────────────────────────┤
│ BottomTabBar │ ← Fixed bottom (mobile), Sidebar (desktop)
└─────────────────────────┘
│ FAB (floating) │ ← Bottom-right, above tab bar
│ Toast (floating) │ ← Top-right or bottom-center
```
- Responsive: detect screen width
- Mobile (< 768px): bottom tab bar, content full width
- Desktop (>= 768px): sidebar left, content fills remaining width — UI-04
**2. Persistent Header** (`frontend/src/lib/components/Header.svelte`) — UI-03:
- Fixed at top, always visible
- Content (reactive, from tournament state):
- Clock: large countdown timer (MM:SS format, red text in final 10s)
- Current level number and name (e.g., "Level 5 — NL Hold'em")
- Blinds: SB/BB display (e.g., "100/200")
- Ante: if > 0, show ante (e.g., "Ante 25")
- Player count: "12/20 remaining" (active/total)
- Pause indicator: pulsing "PAUSED" when clock is paused
- Break indicator: "BREAK" with different styling when on break level
- Compact on mobile (smaller font, abbreviated), expanded on desktop
- Connected to tournament state store (auto-updates from WebSocket)
**3. Bottom Tab Bar** (`frontend/src/lib/components/BottomTabs.svelte`) — UI-01:
- 5 tabs: Overview, Players, Tables, Financials, More
- Each tab: icon + label
- Active tab highlighted with accent color
- 48px touch targets — UI-06
- Renders only on mobile (hidden on desktop where sidebar shows)
- Navigation: SvelteKit goto() or <a> elements
**4. Desktop Sidebar** — UI-04:
- Same 5 navigation items as bottom tabs but in vertical sidebar
- Wider labels, no icons-only mode
- Active item highlighted
- Renders only on desktop (>= 768px)
**5. Floating Action Button** (`frontend/src/lib/components/FAB.svelte`) — UI-02:
- Positioned bottom-right, above the tab bar
- Default state: single button with "+" icon
- Expanded state: fan out action buttons:
- Bust (red) — opens bust-out flow
- Buy In (green) — opens buy-in flow
- Rebuy (blue) — opens rebuy flow
- Add-On (yellow) — opens add-on flow
- Pause/Resume (orange) — toggles clock
- Each action button: 48px, with label
- Press-state animation (scale down on press) — UI-06
- Context-aware: only show relevant actions (e.g., hide "Add-On" if not in addon window)
- Close on backdrop tap or ESC
**6. Toast Notifications** (`frontend/src/lib/components/Toast.svelte`) — UI-07:
- Toast state using Svelte 5 runes:
```typescript
class ToastState {
toasts = $state<Toast[]>([]);
success(message: string, duration?: number) { ... }
info(message: string, duration?: number) { ... }
warning(message: string, duration?: number) { ... }
error(message: string, duration?: number) { ... }
dismiss(id: string) { ... }
}
export const toast = new ToastState();
```
- Auto-dismiss: success (3s), info (4s), warning (5s), error (manual dismiss or 8s)
- Stacking: multiple toasts stack vertically
- Animation: slide in from right, fade out
- Color coding: green (success), blue (info), yellow (warning), red (error) — using Catppuccin colors
**7. Data Table** (`frontend/src/lib/components/DataTable.svelte`) — UI-08:
- Props: columns config, data array, sortable flag, searchable flag
- Features:
- Sort by clicking column header (asc/desc toggle)
- Sticky header on scroll
- Search/filter input (filters across all visible columns)
- Row click handler (for detail navigation)
- Mobile: swipe actions (swipe left reveals action buttons like "Bust", "Rebuy")
- Loading state: skeleton rows
- Empty state: "No data" message
- Responsive: hide less important columns on mobile (configurable per column)
- 48px row height for touch targets — UI-06
**8. Multi-Tournament Tabs:**
- Show tabs at top of content area when 2+ tournaments are active
- Each tab: tournament name + status indicator
- Tapping a tab switches the active tournament (changes which state the views render)
- Keep both tournament states in memory (keyed by tournament ID) for fast switching — don't clear/re-fetch on tab change
- WebSocket subscribes to all active tournaments simultaneously; messages route to the correct state by tournament ID
- On phone: scrollable horizontal tabs
**9. Loading States** — UI-06:
- Skeleton loading component: animated placeholder matching content shape
- Used in all data-fetching views
- Full-page loading spinner for initial app load
- Inline loading states for buttons (spinner replaces label during action)
**Verification:**
- App renders with Catppuccin Mocha dark theme
- Header shows clock countdown (updates from WebSocket)
- Bottom tabs navigate between Overview/Players/Tables/Financials/More on mobile
- Sidebar navigation works on desktop
- FAB expands to show action buttons
- Toast notifications appear and auto-dismiss
- Data table sorts, filters, and handles mobile swipe actions
- Multi-tournament tabs appear when 2+ tournaments exist
- All interactive elements meet 48px minimum touch target
</task>
## Verification Criteria
1. Mobile-first bottom tab bar with Overview, Players, Tables, Financials, More
2. FAB expands to show quick actions (Bust, Buy In, Rebuy, Add-On, Pause/Resume)
3. Persistent header shows clock, level, blinds, player count — updates in real time
4. Desktop sidebar navigation for wider screens
5. Toast notifications work (success, info, warning, error) with auto-dismiss
6. Data tables with sort, sticky header, search/filter, swipe actions (mobile)
7. Multi-tournament tabs appear when 2+ tournaments active
8. All interactive elements meet 48px minimum touch targets
9. Loading states (skeleton, spinner) for all data-fetching views
## Must-Haves (Goal-Backward)
- [ ] Mobile-first bottom tab bar with 5 navigation tabs
- [ ] FAB with context-aware quick actions
- [ ] Persistent header with live clock, level, blinds, player count
- [ ] Responsive layout (bottom tabs on mobile, sidebar on desktop)
- [ ] Data table component reusable across all views
- [ ] Toast notification system