Files
Orchestrator/.kiro/specs/jury-voting/tasks.md
T
2026-06-30 20:37:40 +02:00

15 KiB
Raw Blame History

Implementation Plan: Jury Voting

Overview

Build a full-stack web application for the 15. UNIKAT Ideenwettbewerb jury voting process. The app enables 6 jurors to score 11 teams on 5 criteria (110) via smartphones, with real-time results displayed on a beamer. Stack: React 19 + Vite frontend, Express.js backend, JSON file persistence, SSE for live updates. TypeScript throughout.

Tasks

  • 1. Project scaffolding and configuration

    • 1.1 Initialize Vite + React 19 + TypeScript project in Jury-Voting/

      • Run npm create vite@latest with React + TypeScript template
      • Configure vite.config.ts with proxy to Express backend (port 3001)
      • Add dependencies: react-router-dom, lucide-react
      • Add dev dependencies: vitest, fast-check, @testing-library/react, jsdom
      • Requirements: 5.2 (dark theme, Outfit font, glassmorphism)
    • 1.2 Set up Express.js backend in Jury-Voting/server/

      • Create server/ directory with TypeScript configuration
      • Install: express, cors, tsx (for dev), typescript
      • Create server/index.ts entry point with basic Express app on port 3001
      • Add npm run dev:server script using tsx watch
      • Requirements: 3.1 (score persistence)
    • 1.3 Define shared TypeScript interfaces in Jury-Voting/shared/types.ts

      • Create Session, Team, Juror, Criterion, Score, TeamResult, PendingScore interfaces
      • Create SessionConfig interface with criteria and scale settings
      • Export all types for use by both frontend and backend
      • Requirements: 6.2, 6.3 (configurable teams, criteria, scale)
    • 1.4 Create seed data file Jury-Voting/server/data/seed.json

      • Pre-populate 11 teams from Teams/ folder (id, number, name, description)
      • Pre-populate 6 jurors (Cielejewski, Finke, Freyer, Knie, Meyer, Trieschmann)
      • Pre-populate 5 criteria (Gesamtidee, Kundennutzen, Markt, Realisierbarkeit, Präsentation)
      • Set default session config (scale 110)
      • Requirements: 6.2, 6.3, 6.6
  • 2. Backend data layer and core logic

    • 2.1 Implement JSON file store (server/store.ts)

      • Read/write server/data/session.json atomically (write to temp, rename)
      • Create .bak backup before each write
      • Initialize from seed.json if session.json doesn't exist
      • Expose typed read/write functions for session, teams, jurors, scores
      • Requirements: 3.1, 3.2 (persistence, reliability)
    • 2.2 Implement score aggregation logic (server/aggregation.ts)

      • calculateTeamResult(teamId, scores[]) → total, average, jurorCount, complete
      • calculateRankings(teamResults[]) → sorted results with dense ranking (ties get same rank)
      • getCompletionStatus(jurorId, teamId, scores[]) → 'complete' | 'partial' | 'pending'
      • Pure functions, no side effects — easy to test
      • Requirements: 4.2, 4.3, 4.4, 4.5
    • * 2.3 Write property test: Score Aggregation Correctness

      • Property 4: Score Aggregation Correctness
      • Generate random valid scores (110) for arbitrary juror/team/criterion combinations
      • Assert: total equals arithmetic sum of all score values for a team
      • Assert: average equals total / distinct jurors who scored
      • Validates: Requirements 4.2, 4.3
    • * 2.4 Write property test: Ranking Correctness with Ties

      • Property 5: Ranking Correctness with Ties
      • Generate random team totals, compute rankings
      • Assert: rankings sorted descending by total
      • Assert: equal totals receive equal rank numbers
      • Validates: Requirements 4.4, 4.5
    • * 2.5 Write property test: Team Completion Status

      • Property 1: Team Completion Status
      • Generate random subsets of criteria scores for a juror-team pair
      • Assert: 5 scores → 'complete', 14 → 'partial', 0 → 'pending'
      • Validates: Requirements 2.4, 7.1
    • 2.6 Implement last-write-wins conflict resolution (server/conflict.ts)

      • Compare timestamps on incoming score vs stored score
      • Accept score with later updatedAt timestamp
      • Return conflict info (409) if client sends older timestamp without force flag
      • Requirements: 3.3, 3.4
    • * 2.7 Write property test: Last-Write-Wins Score Resolution

      • Property 3: Last-Write-Wins Score Resolution
      • Generate sequences of score updates with varying timestamps for same (jurorId, teamId, criterionId)
      • Assert: final persisted value equals the value with the most recent timestamp
      • Validates: Requirements 3.3
  • 3. Backend API endpoints

    • 3.1 Implement session endpoints

      • GET /api/session — return current session state
      • POST /api/session/start — set status to 'active', record startedAt
      • POST /api/session/end — set status to 'ended', record endedAt
      • Validate: cannot start if already active, cannot end if not active
      • Requirements: 6.4, 6.5
    • 3.2 Implement team and juror endpoints

      • GET /api/teams — return all teams
      • GET /api/jurors — return all jurors
      • Requirements: 6.2, 6.6
    • 3.3 Implement score endpoints

      • POST /api/scores — submit/update a score (validate: session active, score 110, valid jurorId/teamId/criterionId)
      • GET /api/scores/:jurorId — return all scores for a juror
      • GET /api/scores — return all scores (admin only)
      • On successful score submission, trigger SSE broadcast
      • Requirements: 2.3, 3.1, 3.3
    • * 3.4 Write property test: Session Gate

      • Property 6: Session Gate
      • Generate score submissions with random session states ('setup', 'active', 'ended')
      • Assert: accepted if and only if session status is 'active'
      • Validates: Requirements 6.4, 6.5
    • 3.5 Implement results and progress endpoints

      • GET /api/results — return aggregated TeamResult[] with rankings
      • GET /api/progress — return juror completion matrix (jurorId → count of fully-scored teams)
      • GET /api/export/csv — generate and return CSV file
      • Progress endpoint must NOT include individual score values
      • Requirements: 4.1, 7.1, 7.3, 8.1
    • * 3.6 Write property test: Progress Privacy

      • Property 7: Progress Privacy
      • Generate random score sets, call progress endpoint logic
      • Assert: response contains juror completion counts but no individual score values
      • Validates: Requirements 7.3
  • 4. SSE real-time infrastructure

    • 4.1 Implement SSE endpoint (server/sse.ts)

      • GET /api/results/stream — establish SSE connection
      • Maintain list of connected clients
      • On score change, broadcast scores_updated event with full results payload
      • Handle client disconnect (remove from list)
      • Set appropriate headers: Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive
      • Requirements: 4.1, 5.3
    • 4.2 Create frontend SSE hook (src/hooks/useSSE.ts)

      • Custom React hook that connects to /api/results/stream
      • Parse incoming events and update local state
      • Auto-reconnect on connection loss (EventSource built-in)
      • Return: { results, connected, lastUpdate }
      • Requirements: 5.3 (update within 2 seconds without page refresh)
  • 5. Checkpoint - Backend complete

    • Ensure all tests pass, ask the user if questions arise.
  • 6. Frontend: Juror selection and routing

    • 6.1 Set up React Router with route structure

      • Configure routes: /, /vote, /vote/:teamId, /results, /admin
      • Create App.tsx with BrowserRouter and route definitions
      • Create layout wrapper with dark theme base styles
      • Requirements: 1.1, 2.1
    • 6.2 Implement JurorSelect page (src/pages/JurorSelect.tsx)

      • Fetch juror list from GET /api/jurors
      • Display 6 juror names as large glass-panel cards (tap targets for mobile)
      • On selection: store jurorId in localStorage, navigate to /vote
      • Show warning if juror already has active session (allow override)
      • Requirements: 1.1, 1.2, 1.3, 1.4
    • * 6.3 Write unit tests for JurorSelect

      • Test: renders 6 juror names
      • Test: selection stores jurorId and navigates
      • Test: shows conflict warning for active juror
      • Requirements: 1.1, 1.2, 1.3
  • 7. Frontend: Voting interface

    • 7.1 Implement VotingDashboard page (src/pages/VotingDashboard.tsx)

      • Fetch teams from GET /api/teams and scores from GET /api/scores/:jurorId
      • Display 11 TeamCard components in a scrollable list
      • Each card shows: team name, description snippet, status icon (✓ complete / ◐ partial / ○ pending)
      • Tap card navigates to /vote/:teamId
      • Requirements: 2.1, 2.4, 2.5
    • 7.2 Implement TeamScoring page (src/pages/TeamScoring.tsx)

      • Display team name and description at top
      • Render 5 ScoreSlider components (one per criterion)
      • Each slider: range 110, shows current value, large touch target
      • Auto-save on change with 300ms debounce
      • Show green flash confirmation on successful save
      • Prev/Next team navigation buttons
      • Requirements: 2.2, 2.3, 2.5
    • 7.3 Implement offline score queue (src/hooks/useOfflineQueue.ts)

      • On score change: save to localStorage pendingScores array immediately
      • Attempt POST to /api/scores — on success, mark as synced
      • On network failure: keep in queue, show subtle "offline" indicator
      • On reconnect: replay pending scores in order
      • Exponential backoff for retries (max 3 attempts per score)
      • Requirements: 3.1, 3.2
    • * 7.4 Write property test: Offline Queue Preservation

      • Property 2: Offline Queue Preservation
      • Generate random sequences of scores submitted while "offline"
      • Simulate sync, assert all scores present on server with original values/timestamps
      • Validates: Requirements 3.2
    • * 7.5 Write unit tests for TeamScoring

      • Test: renders 5 criteria with correct labels
      • Test: auto-saves on slider change (debounced)
      • Test: shows confirmation flash on save
      • Test: prev/next navigation works
      • Requirements: 2.2, 2.3
  • 8. Frontend: Results view (beamer)

    • 8.1 Implement ResultsView page (src/pages/ResultsView.tsx)

      • Full-screen layout, no navigation chrome
      • Connect to SSE via useSSE hook
      • Display RankingTable component with animated transitions
      • Show "Voting in Progress" or "Final Results" based on session status
      • Optimized for 1920×1080 landscape projection
      • Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6
    • 8.2 Implement RankingTable component (src/components/RankingTable.tsx)

      • Display ranked list: rank, team name, total score, average score
      • Animate rank changes (CSS transitions on position)
      • Highlight top 3 teams with special styling
      • Handle ties (same rank number displayed)
      • Requirements: 4.4, 4.5, 5.4
    • * 8.3 Write property test: Results Display Completeness

      • Property 9: Results Display Completeness
      • Generate random TeamResult objects, render ResultsView
      • Assert: each team's name, total, average, and rank are present in rendered output
      • Validates: Requirements 5.4
  • 9. Frontend: Admin panel

    • 9.1 Implement AdminPanel page (src/pages/AdminPanel.tsx)

      • Session controls: Start Voting / End Voting buttons with confirmation dialogs
      • Show current session status and timestamps
      • Warning when ending session with incomplete scores
      • Requirements: 6.1, 6.4, 6.5
    • 9.2 Implement ProgressMatrix component (src/components/ProgressMatrix.tsx)

      • Fetch from GET /api/progress
      • Display 6×11 grid (jurors × teams) with completion indicators
      • Show per-juror totals (X/11 teams complete)
      • Indicate when all voting is complete (6/6 jurors × 11/11 teams)
      • No individual scores shown — only completion status
      • Requirements: 7.1, 7.2, 7.3
    • 9.3 Implement CSV export functionality

      • Export button calls GET /api/export/csv and triggers download
      • CSV format: teams as rows, (juror × criterion) as columns, plus totals/averages/ranks
      • Match structure of original Auswertungstabelle
      • Requirements: 8.1, 8.2, 8.3, 8.4
    • * 9.4 Write property test: Export Round-Trip

      • Property 8: Export Round-Trip
      • Generate complete score sets, export to CSV, parse CSV back
      • Assert: all individual scores recovered, totals correct, averages correct, rankings correct
      • Validates: Requirements 8.1, 8.2, 8.3, 8.4
  • 10. Checkpoint - Core features complete

    • Ensure all tests pass, ask the user if questions arise.
  • 11. Styling and visual polish

    • 11.1 Implement global theme and CSS variables

      • Define CSS custom properties: --bg-primary: #0a0a0a, --accent: #00FF00, glassmorphism values
      • Import Outfit font from Google Fonts
      • Set up glassmorphism utility classes (backdrop-filter, border, shadow)
      • Dark scrollbar styling, smooth transitions
      • Requirements: 5.2
    • 11.2 Style all components to match Projekt-KIQ-HP design

      • JurorSelect: glass-panel cards with hover/tap glow effect
      • VotingDashboard: team cards with status indicators, neon green accents
      • TeamScoring: custom slider styling (neon green track), score display
      • ResultsView: large typography, animated rank transitions, glassmorphism table
      • AdminPanel: clean grid layout, status badges
      • Mobile-first responsive design (320px428px primary, scales up)
      • Requirements: 2.1, 5.1, 5.2
  • 12. Deployment configuration

    • 12.1 Create production build setup

      • Configure Vite to build static assets to dist/
      • Configure Express to serve static files from dist/ in production
      • Single-port deployment: Express serves both API and frontend
      • Add npm run build and npm start scripts
      • Requirements: all (deployment readiness)
    • 12.2 Create docker-compose.yml for containerized deployment

      • Single container: Node.js serving Express + static frontend
      • Volume mount for server/data/ (persist scores across restarts)
      • Expose port 3001 (or configurable via env)
      • Add .dockerignore and Dockerfile
      • Requirements: all (deployment readiness)
  • 13. Final checkpoint - All features complete

    • Ensure all tests pass, ask the user if questions arise.

Notes

  • Tasks marked with * are optional property-based tests — skip for faster MVP delivery given the tight timeline (event: 19. Mai 2026)
  • Each task references specific requirements for traceability
  • Checkpoints at tasks 5, 10, and 13 ensure incremental validation
  • Property tests validate the 9 correctness properties from the design document
  • The app is designed for single-event use — simplicity over scalability
  • All seed data (teams, jurors, criteria) is pre-populated; no manual setup needed at event time