docs: add Phase 1.6 Competition Heats System implementation plan
This commit is contained in:
119
docs/TODO.md
119
docs/TODO.md
@@ -6,9 +6,10 @@
|
||||
|
||||
## 🎯 CURRENT STATUS
|
||||
|
||||
**Phase:** 1.5 (Email & WSDC & Profiles & Security) - ✅ COMPLETED
|
||||
**Next Phase:** 2 (Core Features) - ⏳ PENDING
|
||||
**Progress:** ~65% complete
|
||||
**Phase:** 1.6 (Competition Heats System) - ⏳ IN PROGRESS
|
||||
**Previous Phase:** 1.5 (Email & WSDC & Profiles & Security & QR Check-in) - ✅ COMPLETED
|
||||
**Next Phase:** 2 (Core Features - Matches API + Ratings + WebRTC) - ⏳ PENDING
|
||||
**Progress:** ~68% complete
|
||||
|
||||
### ✅ Completed
|
||||
- Phase 0: Frontend mockup with all views
|
||||
@@ -79,6 +80,118 @@
|
||||
|
||||
---
|
||||
|
||||
## 📌 Phase 1.6: Competition Heats System - ⏳ IN PROGRESS
|
||||
|
||||
**Estimated Time:** 6-8 hours
|
||||
**Priority:** HIGH (blocking for proper matchmaking)
|
||||
**Status:** Design phase completed, ready for implementation
|
||||
|
||||
### Business Logic Summary
|
||||
- Users must declare their competition heats before matchmaking
|
||||
- One user can compete in multiple divisions/heats (e.g., J&J Novice + Strictly Advanced)
|
||||
- **Constraint:** Cannot compete in same role in same division+competition type (e.g., cannot have "J&J Novice Leader" twice)
|
||||
- Role is optional (can be NULL = undeclared)
|
||||
- Heat numbers: 1-9
|
||||
- Format example: "J&J NOV 1 L" (Jack & Jill, Novice, Heat 1, Leader)
|
||||
|
||||
### Step 1: Database Schema (1-2h) ⏳
|
||||
- [ ] Create migration for 3 new tables:
|
||||
- `divisions` - Pre-defined competition divisions
|
||||
- Columns: id, name (varchar), abbreviation (varchar 3), display_order (int)
|
||||
- Seed data: Newcomer (NEW), Novice (NOV), Intermediate (INT), Advanced (ADV), All-Star (ALL), Champion (CHA)
|
||||
- `competition_types` - Pre-defined competition types
|
||||
- Columns: id, name (varchar), abbreviation (varchar 3)
|
||||
- Seed data: Jack & Jill (J&J), Strictly (STR)
|
||||
- `event_user_heats` - User's declared heats for event
|
||||
- Columns: id, user_id, event_id, division_id, competition_type_id, heat_number (1-9), role (enum: Leader/Follower/NULL), created_at, updated_at
|
||||
- **UNIQUE constraint:** (user_id, event_id, division_id, competition_type_id, role)
|
||||
- Foreign keys: user_id → users.id, event_id → events.id, division_id → divisions.id, competition_type_id → competition_types.id
|
||||
- Indexes: (user_id, event_id), (event_id)
|
||||
- [ ] Update Prisma schema
|
||||
- [ ] Run migration
|
||||
- [ ] Verify seed data
|
||||
|
||||
### Step 2: Backend API (2-3h) ⏳
|
||||
- [ ] Create routes and controllers:
|
||||
- `GET /api/divisions` - List all divisions (public)
|
||||
- `GET /api/competition-types` - List all competition types (public)
|
||||
- `POST /api/events/:slug/heats` - Add/update user's heats (authenticated)
|
||||
- Input: array of { divisionId, competitionTypeId, heatNumber, role? }
|
||||
- Validation: unique constraint, heat number 1-9
|
||||
- Replace all existing heats (upsert pattern)
|
||||
- `GET /api/events/:slug/heats/me` - Get current user's heats (authenticated)
|
||||
- `GET /api/events/:slug/heats/all` - Get all users' heats for sidebar (authenticated)
|
||||
- Returns: userId, username, avatar, heats[]
|
||||
- `DELETE /api/events/:slug/heats/:id` - Delete specific heat (authenticated)
|
||||
- [ ] Validation middleware:
|
||||
- Heat number 1-9
|
||||
- Role enum (Leader/Follower/NULL)
|
||||
- Unique constraint enforcement
|
||||
- User must be event participant
|
||||
- [ ] Unit tests (CRUD operations, validation, constraints)
|
||||
|
||||
### Step 3: Socket.IO Events (0.5h) ⏳
|
||||
- [ ] Add event: `heats_updated` - Broadcast when user updates heats
|
||||
- Payload: { userId, username, heats[] }
|
||||
- Send to all users in event room
|
||||
- [ ] Update active_users event to include heats data
|
||||
|
||||
### Step 4: Frontend Components (2-3h) ⏳
|
||||
- [ ] Create HeatsBanner component (sticky between header and chat):
|
||||
- Show only if user has no heats declared
|
||||
- Form with dynamic heat entries (add/remove)
|
||||
- Fields per entry: Competition Type (select), Division (select), Heat Number (1-9), Role (optional: Leader/Follower)
|
||||
- "Save Heats" button → POST /api/events/:slug/heats
|
||||
- On save success: hide banner, show success message
|
||||
- [ ] Add "Edit Heats" button in EventChatPage header (next to "Leave Event")
|
||||
- Opens modal with same form as banner
|
||||
- Pre-fill with existing heats
|
||||
- "Update Heats" button
|
||||
- [ ] Update EventChatPage sidebar (Active Users):
|
||||
- Display heat badges under username
|
||||
- Format: "J&J NOV 1 L", "STR ADV 3" (no role if NULL)
|
||||
- Max 3 visible badges, "+" indicator if more
|
||||
- Add checkbox: "Hide users from my heats"
|
||||
- Logic: Hide users with ANY matching (division + competition_type + heat_number)
|
||||
- Disable UserPlus icon if user has no heats declared
|
||||
- [ ] Create frontend API methods in services/api.js:
|
||||
- divisionsAPI.getAll()
|
||||
- competitionTypesAPI.getAll()
|
||||
- heatsAPI.saveHeats(slug, heats[])
|
||||
- heatsAPI.getMyHeats(slug)
|
||||
- heatsAPI.getAllHeats(slug)
|
||||
- heatsAPI.deleteHeat(slug, heatId)
|
||||
- [ ] Socket.IO integration:
|
||||
- Listen to `heats_updated` event
|
||||
- Update active users list in real-time
|
||||
|
||||
### Step 5: Styling & UX (0.5-1h) ⏳
|
||||
- [ ] Heat badges design (color-coded by division?)
|
||||
- [ ] Banner responsive design (mobile + desktop)
|
||||
- [ ] Modal for editing heats
|
||||
- [ ] Loading states for heat operations
|
||||
- [ ] Error handling & validation messages
|
||||
- [ ] Empty states ("No heats declared yet")
|
||||
|
||||
### Step 6: Testing & Edge Cases (0.5-1h) ⏳
|
||||
- [ ] Test unique constraint violation (frontend + backend)
|
||||
- [ ] Test filter "Hide users from my heats"
|
||||
- [ ] Test real-time updates when someone changes heats
|
||||
- [ ] Test UserPlus button disabled for users without heats
|
||||
- [ ] Test banner dismissal and re-opening via "Edit Heats"
|
||||
- [ ] Test multiple heats display in sidebar
|
||||
- [ ] Test role optional (NULL) handling
|
||||
|
||||
### Technical Notes
|
||||
- **Abbreviations:**
|
||||
- Divisions: NEW, NOV, INT, ADV, ALL, CHA
|
||||
- Competition Types: J&J, STR
|
||||
- Roles: L (Leader), F (Follower), empty (NULL)
|
||||
- **Display format:** "{CompType} {Div} {Heat} {Role?}" → "J&J NOV 1 L"
|
||||
- **Future enhancement:** When Matches API is implemented, editing heats with active match requires partner confirmation
|
||||
|
||||
---
|
||||
|
||||
## 📌 NEXT STEPS - Phase 2: Core Features
|
||||
|
||||
**Estimated Time:** 12-15 hours
|
||||
|
||||
Reference in New Issue
Block a user