# TODO - spotlight.cam **Active tasks and roadmap - optimized for quick reference** --- ## 🎯 CURRENT STATUS **Phase:** 2.5 (WebRTC Implementation) - ⏳ NEXT **Previous Phase:** 2 (Matches & Ratings API) - ✅ COMPLETED **Also Completed:** 1.6 (Competition Heats System - Backend & Frontend) - ✅ COMPLETED **Progress:** ~72% complete ### ✅ Completed - Phase 0: Frontend mockup with all views - Phase 1: Backend Foundation - Node.js + Express API - PostgreSQL database with Prisma ORM - JWT authentication (register, login) - Socket.IO real-time chat (event & match rooms) - Comprehensive test coverage (81%+) - Phase 1.5: Email & WSDC & Profiles & Security - Email verification (AWS SES with link + PIN) - Password reset workflow - WSDC API integration (auto-fill registration) - User profiles (social media links, location) - Public profiles (/{username}) - Event participation tracking (auto-save joined events) - Event security (unique slugs, prevent ID enumeration) - QR code event check-in system (physical presence required at venue) - Phase 1.6: Competition Heats System - Database schema (divisions, competition_types, event_user_heats) - Backend API (CRUD operations, validation) - Frontend components (HeatsBanner, forms, badges) - Phase 2: Matches & Ratings API - Match requests with CUID slugs - Real-time match notifications - Ratings system (1-5 stars, comments, preferences) - Public profile ratings display - Profile links from chat/matches - Message history persistence - Duplicate rating prevention ### ⏳ Next Priority **WebRTC Implementation** - P2P signaling and file transfer **See:** `docs/COMPLETED.md` for full list of completed tasks --- ## 📌 Phase 1: Backend Foundation - ✅ COMPLETED **Completed:** 2025-11-12 **Time Spent:** ~14 hours ### ✅ Step 1: Backend Setup (COMPLETED) - [x] Add `backend` service to docker-compose.yml - [x] Initialize Node.js + Express project - [x] Create folder structure (routes, controllers, middleware, etc.) - [x] Add healthcheck endpoint: `GET /api/health` - [x] Update nginx config to proxy `/api/*` to backend - [x] Unit tests (7 tests passing) ### ✅ Step 2: PostgreSQL Setup (COMPLETED) - [x] Add `db` service (PostgreSQL 15) to docker-compose.yml - [x] Configure volumes for data persistence - [x] Prisma ORM implementation - [x] Create database schema (6 tables with relations) - [x] Add indexes for performance - [x] Create seed data (3 events, 2 users, chat rooms) - [x] Fix OpenSSL compatibility issue for Prisma ### ✅ Step 3: Authentication API (COMPLETED) - [x] Install dependencies: bcrypt, jsonwebtoken, express-validator - [x] Implement password hashing with bcrypt (10 salt rounds) - [x] Implement JWT token generation and verification - [x] Create endpoints: register, login, /users/me - [x] Create auth middleware for protected routes - [x] Update frontend AuthContext to use real API - [x] Unit tests (30 tests, 78%+ coverage) ### ✅ Step 4: WebSocket Chat (COMPLETED) - [x] Install Socket.IO 4.8.1 on backend - [x] Setup Socket.IO server with Express integration - [x] JWT authentication for socket connections - [x] Event rooms (join/leave/messages/active users) - [x] Match rooms (private 1:1 chat) - [x] Install socket.io-client on frontend - [x] Update EventChatPage with real-time messaging - [x] Update MatchChatPage with real-time chat - [x] Unit tests (12 tests, 89% coverage for Socket.IO module) --- ## 📌 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) ⏳ - [x] Create HeatsBanner component (sticky between header and chat) - ✅ DONE - [x] Show only if user has no heats declared - [x] Form with dynamic heat entries (add/remove) - [x] Fields per entry: Competition Type (select), Division (select), Heat Number (1-9), Role (optional: Leader/Follower) - [x] "Save Heats" button → POST /api/events/:slug/heats - [x] On save success: hide banner, show success message - [ ] Add "Edit Heats" button in EventChatPage header (next to "Leave Event") - ⏳ TODO - Opens modal with same form as banner - Pre-fill with existing heats - "Update Heats" button - [ ] Update EventChatPage sidebar (Active Users) - ⏳ TODO - 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 - [x] Create frontend API methods in services/api.js - ✅ DONE - [x] divisionsAPI.getAll() - [x] competitionTypesAPI.getAll() - [x] heatsAPI.saveHeats(slug, heats[]) - [x] heatsAPI.getMyHeats(slug) - [x] heatsAPI.getAllHeats(slug) - [x] heatsAPI.deleteHeat(slug, heatId) - [ ] Socket.IO integration - ⏳ TODO - Listen to `heats_updated` event - Update active users list in real-time ### Step 4.1: EventChatPage Integration - ⏳ IN PROGRESS (Remaining work) **What needs to be done:** 1. **Add state management for heats:** ```javascript const [myHeats, setMyHeats] = useState([]); const [userHeats, setUserHeats] = useState(new Map()); // userId → heats[] const [showHeatsBanner, setShowHeatsBanner] = useState(false); const [hideMyHeats, setHideMyHeats] = useState(false); const [showHeatsModal, setShowHeatsModal] = useState(false); ``` 2. **Load heats on component mount:** ```javascript useEffect(() => { const loadHeats = async () => { const [myHeatsData, allHeatsData] = await Promise.all([ heatsAPI.getMyHeats(slug), heatsAPI.getAllHeats(slug), ]); setMyHeats(myHeatsData); setShowHeatsBanner(myHeatsData.length === 0); // Map userHeats const heatsMap = new Map(); allHeatsData.forEach(userHeat => { heatsMap.set(userHeat.userId, userHeat.heats); }); setUserHeats(heatsMap); }; loadHeats(); }, [slug]); ``` 3. **Add HeatsBanner before chat:** ```jsx {showHeatsBanner && ( { setShowHeatsBanner(false); // Reload heats }} /> )} ``` 4. **Add "Edit Heats" button in header (next to "Leave Event"):** ```jsx ``` 5. **Create modal for editing heats (reuse HeatsBanner logic)** 6. **Add heat badges to sidebar under username:** ```jsx {activeUsers.map(activeUser => { const userHeatsForThisUser = userHeats.get(activeUser.userId) || []; const hasHeats = userHeatsForThisUser.length > 0; return (
{activeUser.username}
{/* Heat badges */}
{userHeatsForThisUser.slice(0, 3).map(heat => ( {heat.competitionType.abbreviation} {heat.division.abbreviation} {heat.heatNumber} {heat.role && ` ${heat.role[0]}`} ))} {userHeatsForThisUser.length > 3 && ( +{userHeatsForThisUser.length - 3} )}
{/* UserPlus button - disabled if no heats */}
); })} ``` 7. **Add filter checkbox above active users:** ```jsx ``` 8. **Filter logic:** ```javascript const filteredUsers = hideMyHeats ? activeUsers.filter(activeUser => { const theirHeats = userHeats.get(activeUser.userId) || []; return !theirHeats.some(theirHeat => myHeats.some(myHeat => myHeat.divisionId === theirHeat.divisionId && myHeat.competitionTypeId === theirHeat.competitionTypeId && myHeat.heatNumber === theirHeat.heatNumber ) ); }) : activeUsers; ``` 9. **Socket.IO heats_updated listener:** ```javascript useEffect(() => { const socket = getSocket(); if (!socket) return; socket.on('heats_updated', ({ userId, username, heats }) => { setUserHeats(prev => { const newMap = new Map(prev); newMap.set(userId, heats); return newMap; }); // If it's current user, update myHeats if (userId === user.id) { setMyHeats(heats); setShowHeatsBanner(heats.length === 0); } }); return () => { socket.off('heats_updated'); }; }, [user.id]); ``` ### 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.5: WebRTC Implementation **Estimated Time:** 8-10 hours **Priority:** HIGH ### Step 1: WebRTC Signaling (3-4h) ⏳ - [ ] Add Socket.IO signaling events: - `webrtc_offer` - Send SDP offer - `webrtc_answer` - Send SDP answer - `webrtc_ice_candidate` - Exchange ICE candidates - [ ] Frontend WebRTC setup: - RTCPeerConnection initialization - STUN server configuration - Signaling flow implementation - [ ] Connection state monitoring - [ ] Unit tests (signaling message exchange) ### Step 2: WebRTC File Transfer (4-5h) ⏳ - [ ] RTCDataChannel setup (ordered, reliable) - [ ] File metadata exchange (name, size, type) - [ ] File chunking implementation (16KB chunks) - [ ] Progress monitoring (sender & receiver) - [ ] Error handling & reconnection logic - [ ] Complete P2P video transfer flow: - Select video file - Establish P2P connection - Transfer file via DataChannel - Save file on receiver side - [ ] Test with various file sizes - [ ] Fallback: Link sharing (already implemented in UI) --- ## 🎯 Future Phases (Reference) ### Phase 3: MVP Finalization (2-3 weeks) - [ ] Security hardening: - Rate limiting (express-rate-limit) - Input validation & sanitization - CORS configuration - SQL injection prevention - XSS protection - [ ] Testing: - Integration tests (API endpoints) - E2E tests (Playwright/Cypress) - WebRTC connection tests - [ ] PWA features: - Web app manifest - Service worker (offline support) - App icons & splash screens - Install prompt - [ ] Deployment: - Production Docker images - Environment configuration - Database backups - Monitoring & logging - CI/CD pipeline ### Phase 5: Optional Extensions - [ ] User badges & trust system - [ ] Block users - [ ] Public profiles - [ ] Push notifications - [ ] Video compression - [ ] Multi-file transfer --- ## 📝 Active Development Tasks ### Documentation - [x] ✅ README.md - [x] ✅ QUICKSTART.md - [x] ✅ CONTEXT.md - [x] ✅ TODO.md - [x] ✅ SESSION_CONTEXT.md - [x] ✅ ARCHITECTURE.md - [x] ✅ COMPLETED.md - [x] ✅ RESOURCES.md - [ ] ⏳ API documentation (Swagger/OpenAPI) - after backend - [ ] ⏳ Architecture diagrams - after backend - [ ] ⏳ WebRTC flow diagram - after WebRTC implementation ### Infrastructure - [x] ✅ Docker Compose (nginx, frontend) - [ ] ⏳ Docker Compose (backend, db) - [ ] ⏳ Production Dockerfile optimization (multi-stage builds) - [ ] ⏳ CI/CD pipeline (GitHub Actions) - [ ] ⏳ HTTPS setup (Let's Encrypt) ### Testing - [ ] ⏳ Backend tests (Jest + Supertest) - [ ] ⏳ Frontend tests (Vitest + React Testing Library) - [ ] ⏳ E2E tests (Playwright / Cypress) - [ ] ⏳ WebRTC manual testing (different devices) --- ## 🚀 Quick Commands **Start development:** ```bash docker compose up --build ``` **Rebuild after changes:** ```bash docker compose down && docker compose up --build ``` **Access:** - Frontend: http://localhost:8080 - Backend (future): http://localhost:8080/api **Git workflow:** ```bash git status git add . git commit -m "feat: description" ``` --- ## 📊 Progress Tracking | Phase | Status | Progress | Estimated Time | |-------|--------|----------|----------------| | Phase 0: Frontend Mockup | ✅ Done | 100% | ~8h (completed) | | Phase 1: Backend Foundation | ✅ Done | 100% | ~14h (completed) | | Phase 1.5: Email & WSDC & Profiles | ✅ Done | 100% | ~12h (completed) | | Phase 1.6: Competition Heats | ✅ Done | 100% | ~8h (completed) | | Phase 2: Matches & Ratings API | ✅ Done | 100% | ~10h (completed) | | Phase 2.5: WebRTC Implementation | ⏳ Next | 0% | ~8-10h | | Phase 3: MVP Finalization | ⏳ Pending | 0% | ~15-20h | | Phase 4: Extensions | ⏳ Pending | 0% | TBD | **Overall Progress:** ~72% (Phases 0, 1, 1.5, 1.6, 2 completed) --- ## 📝 Notes - Frontend mockup is presentation-ready - All views work with mock data - easy to connect real API - WebRTC P2P mockup in MatchChatPage - needs real implementation - Focus on Phase 1 next (backend foundation) - Update task status: ⏳ → 🔄 → ✅ --- **For detailed task history:** See `docs/COMPLETED.md` **For learning resources:** See `docs/RESOURCES.md` **For quick session context:** See `docs/SESSION_CONTEXT.md` **For technical details:** See `docs/ARCHITECTURE.md` --- **Last Updated:** 2025-11-14