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

8.3 KiB

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

**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) ├─────────────────────────┤ │ │ │ │ ← 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 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:
    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

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