Files
spotlightcam/docs/TODO.md
Radosław Gierwiało fd0dcdf77f test: improve test cleanup with selective deletion
- Replace deleteMany({}) with selective deletion by username/email/slug in:
  - events.test.js (target specific test users/events only)
  - matches.test.js (target specific test users/events only)
  - csrf.test.js (target csrftest user only)
- Replace delete() with deleteMany() for resilient cleanup:
  - matches.test.js (2 inline cleanups)
  - socket-webrtc.test.js (1 inline cleanup)
- Update TODO.md with test status and future UX/UI improvements

Test improvement: 189/223 passing (84.8%), up from 145/223 (65%)
2025-11-20 00:05:24 +01:00

21 KiB

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

🔧 CURRENT STATUS - Backend Tests (2025-11-20)

Test Status: 189/223 passing (84.8%) - Significant improvement!

  • Before: 145/223 (65%)
  • After fixes: 189/223 (84.8%)
  • Improvement: +44 tests (+19.8%)

Fixed:

  1. Test cleanup - replaced deleteMany({}) with selective deletion:
    • backend/src/__tests__/events.test.js - selective deletion by username/email/slug
    • backend/src/__tests__/matches.test.js - selective deletion by username/email/slug
    • backend/src/__tests__/csrf.test.js - selective deletion by username/email
  2. Cleanup resilience - replaced delete() with deleteMany():
    • backend/src/__tests__/socket.test.js - all afterAll + inline cleanup
    • backend/src/__tests__/matches.test.js - inline cleanup (2 locations)
    • backend/src/__tests__/socket-webrtc.test.js - inline cleanup
  3. Socket tests fixes:
    • Changed eventIdslug in socket event handlers
    • Added EventParticipant creation before join_event_room
    • Added safety checks before creating FK relationships
  4. Auth tests fixes:
    • app.test.js - CORS origin localhost:8080
    • security.js - added localhost:3000 to allowed origins (dev)
    • auth-phase1.5.test.js - fixed error message expectations

Remaining issues (34 failing tests in 3 test suites):

  • users.test.js - ??? (unknown errors, needs analysis)
  • events.test.js - ??? (unknown errors, despite cleanup fixes)
  • matches.test.js - PASS individually, FAIL in suite (race conditions?)

TODO:

  1. Check detailed errors in users.test.js
  2. Check detailed errors in events.test.js
  3. Investigate race conditions in matches.test.js
  4. Potentially add test isolation (beforeEach cleanup)
  5. Consider using transactions in tests

Commits made:

  • test: fix socket.test.js cleanup and event room parameters
  • test: improve test cleanup - selective deletion instead of wiping tables

📋 FUTURE UX/UI IMPROVEMENTS

Dashboard Page (Post-Login):

  • Create /dashboard route as default landing page after login
  • Show current user information (profile summary, stats)
  • Quick access buttons to active chats (event rooms, match chats)
  • Display ongoing discussions/conversations
  • Show upcoming events user is registered for
  • Recent activity feed (new matches, messages, ratings)
  • Navigation shortcuts to key features

Event Chat Sidebar Enhancements:

  • Add competitor number display for each user in sidebar
  • Show user nationality (flag + country code)
  • Expand user info cards in sidebar:
    • Competition heats (already implemented)
    • Competitor number
    • Country/nationality
    • User role/level
    • Years of experience (if available in profile)
    • Average rating (if rated)
  • Consider expandable user cards (click to see more details)
  • Add filtering options:
    • By nationality
    • By division/level
    • By competitor number range
  • Improve mobile responsiveness for extended sidebar info

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)

  • Add backend service to docker-compose.yml
  • Initialize Node.js + Express project
  • Create folder structure (routes, controllers, middleware, etc.)
  • Add healthcheck endpoint: GET /api/health
  • Update nginx config to proxy /api/* to backend
  • Unit tests (7 tests passing)

Step 2: PostgreSQL Setup (COMPLETED)

  • Add db service (PostgreSQL 15) to docker-compose.yml
  • Configure volumes for data persistence
  • Prisma ORM implementation
  • Create database schema (6 tables with relations)
  • Add indexes for performance
  • Create seed data (3 events, 2 users, chat rooms)
  • Fix OpenSSL compatibility issue for Prisma

Step 3: Authentication API (COMPLETED)

  • Install dependencies: bcrypt, jsonwebtoken, express-validator
  • Implement password hashing with bcrypt (10 salt rounds)
  • Implement JWT token generation and verification
  • Create endpoints: register, login, /users/me
  • Create auth middleware for protected routes
  • Update frontend AuthContext to use real API
  • Unit tests (30 tests, 78%+ coverage)

Step 4: WebSocket Chat (COMPLETED)

  • Install Socket.IO 4.8.1 on backend
  • Setup Socket.IO server with Express integration
  • JWT authentication for socket connections
  • Event rooms (join/leave/messages/active users)
  • Match rooms (private 1:1 chat)
  • Install socket.io-client on frontend
  • Update EventChatPage with real-time messaging
  • Update MatchChatPage with real-time chat
  • 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)

  • Create HeatsBanner component (sticky between header and chat) - DONE
    • 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") - 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
  • Create frontend API methods in services/api.js - DONE
    • divisionsAPI.getAll()
    • competitionTypesAPI.getAll()
    • heatsAPI.saveHeats(slug, heats[])
    • heatsAPI.getMyHeats(slug)
    • heatsAPI.getAllHeats(slug)
    • 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:

    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:

    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:

    {showHeatsBanner && (
      <HeatsBanner
        slug={slug}
        onSave={() => {
          setShowHeatsBanner(false);
          // Reload heats
        }}
      />
    )}
    
  4. Add "Edit Heats" button in header (next to "Leave Event"):

    <button
      onClick={() => setShowHeatsModal(true)}
      className="flex items-center gap-2 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50"
    >
      <Edit size={16} />
      Edit Heats
    </button>
    
  5. Create modal for editing heats (reuse HeatsBanner logic)

  6. Add heat badges to sidebar under username:

    {activeUsers.map(activeUser => {
      const userHeatsForThisUser = userHeats.get(activeUser.userId) || [];
      const hasHeats = userHeatsForThisUser.length > 0;
    
      return (
        <div key={activeUser.userId}>
          <div className="flex items-center">
            <img src={activeUser.avatar} />
            <span>{activeUser.username}</span>
          </div>
    
          {/* Heat badges */}
          <div className="flex flex-wrap gap-1 mt-1">
            {userHeatsForThisUser.slice(0, 3).map(heat => (
              <span key={heat.id} className="text-xs px-2 py-0.5 bg-blue-100 text-blue-700 rounded">
                {heat.competitionType.abbreviation} {heat.division.abbreviation} {heat.heatNumber}
                {heat.role && ` ${heat.role[0]}`}
              </span>
            ))}
            {userHeatsForThisUser.length > 3 && (
              <span className="text-xs px-2 py-0.5 bg-gray-100 text-gray-600 rounded">
                +{userHeatsForThisUser.length - 3}
              </span>
            )}
          </div>
    
          {/* UserPlus button - disabled if no heats */}
          <button
            onClick={() => handleMatchWith(activeUser.userId)}
            disabled={!hasHeats}
            className="p-1 text-primary-600 hover:bg-primary-50 rounded disabled:opacity-30 disabled:cursor-not-allowed"
          >
            <UserPlus className="w-4 h-4" />
          </button>
        </div>
      );
    })}
    
  7. Add filter checkbox above active users:

    <label className="flex items-center gap-2 text-sm text-gray-700 mb-2">
      <input
        type="checkbox"
        checked={hideMyHeats}
        onChange={(e) => setHideMyHeats(e.target.checked)}
        className="rounded border-gray-300"
      />
      Hide users from my heats
    </label>
    
  8. Filter logic:

    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:

    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)
  • Test TURN servers - Verify NAT traversal for symmetric NAT scenarios
    • Test connection from different network configurations
    • Verify fallback to TURN when direct/STUN fails
    • Monitor ICE candidate gathering and connection state
    • Test with restricted/symmetric NAT environments

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

  • README.md
  • QUICKSTART.md
  • CONTEXT.md
  • TODO.md
  • SESSION_CONTEXT.md
  • ARCHITECTURE.md
  • COMPLETED.md
  • RESOURCES.md
  • API documentation (Swagger/OpenAPI) - after backend
  • Architecture diagrams - after backend
  • WebRTC flow diagram - after WebRTC implementation

Infrastructure

  • 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:

docker compose up --build

Rebuild after changes:

docker compose down && docker compose up --build

Access:

Git workflow:

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