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>
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-042. 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
- Mobile-first bottom tab bar with Overview, Players, Tables, Financials, More
- FAB expands to show quick actions (Bust, Buy In, Rebuy, Add-On, Pause/Resume)
- Persistent header shows clock, level, blinds, player count — updates in real time
- Desktop sidebar navigation for wider screens
- Toast notifications work (success, info, warning, error) with auto-dismiss
- Data tables with sort, sticky header, search/filter, swipe actions (mobile)
- Multi-tournament tabs appear when 2+ tournaments active
- All interactive elements meet 48px minimum touch targets
- 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