* Add oxlint and fix all lint errors
- Install oxlint, add .oxlintrc.json with rules for TypeScript/React
- Add npm run lint / lint:fix scripts
- Add Claude PostToolUse hook to run oxlint on every edited file
- Fix 101 errors: unused vars/imports, eqeqeq, prefer-const, no-new-array
- Fix no-array-index-key (use stable keys or suppress positional cases)
- Fix exhaustive-deps missing dependency in useEffect
- Promote exhaustive-deps and no-array-index-key to errors
- Fix Map.get() !== null bug in $leagueId.server.ts (should be !== undefined)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix no-explicit-any warnings and upgrade tsconfig to ES2023
- Replace all `any` types with proper types or `unknown` across ~20 files
- Add typed socket payload interfaces in draft route and useDraftSocket
- Use any[] with eslint-disable for socket.io callbacks (legitimate escape hatch)
- Bump all tsconfigs from ES2022 → ES2023 to support toSorted/toReversed
- Fix cascading type errors uncovered by removing any: Map.get narrowing,
participant relation types, ChartDataPoint, Partial<NewSeason> indexing
- Add ParticipantResultWithParticipant type to participant-result model
- Fix test fixtures to match updated interfaces (DraftCell, ParticipantResult)
- Fix duplicate getQPStandings import in sportsSeasonId.server.ts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Promote no-explicit-any to error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix bracket point averaging and partial-score display in standings/breakdown
Bracket sports (UCL, NBA, NFL, etc.) use averaged points for tied
positions (e.g. QF losers all share avg of 5th-8th = 20 pts), but
both calculateTeamProjectedScore and getTeamScoreBreakdown were using
raw pointsFor5th (25) instead of the bracket-averaged value. This caused
the team breakdown page to show incorrect actual points (25 vs 20) and
the standings page to show incorrect actualPoints/projectedPoints totals.
Additionally, participants with isPartialScore=true (still alive in a
bracket with a provisional floor position) were being displayed with a
final placement badge (e.g. "5th") instead of "Pending".
Changes:
- calculateTeamProjectedScore: use calculateBracketPoints for bracket
sports; handle isPartialScore participants separately (floor in
actualPoints, incremental EV in projectedPoints, Math.max guard for
EV < floor edge case)
- getTeamScoreBreakdown: same bracket averaging fix; pass isPartialScore
through to picks; use explicit finalPosition != null && > 0 check
- TeamScoreBreakdown component: show Pending badge for isPartialScore
participants; display actual/floor + EV row for all incomplete picks;
add "actual / projected" column header subtitle
- 9 new unit tests covering all scoring branches including the EV-below-
floor clamp edge case
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix typecheck: cast DUMMY_EV_ROW to any in test fixture
ParticipantEV has additional required fields (id, participantId, etc.)
that aren't needed for the mock — cast to any to satisfy the type checker.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace per-sport cards with a single flat table; add Sport column (linked to sport season) before Participant
- Replace three summary stat cards with a compact "X remaining" line in the header
- Format pick numbers as round.pick notation (e.g. 2.02 for pick 16 in a 14-team league)
- Add getNumTeamsInSeason model function to compute pick-within-round correctly
- Remove dead bySport/totalPoints from standings model and component interface
- Narrow standing prop type to only currentRank (placementCounts was unused)
- Guard against numTeams === 0 edge case in pick notation
- Fix 0.0 → 0.00 decimal inconsistency
- Fix unsafe finalPosition! non-null assertion (null treated as Did Not Score)
- Update tests to match current UI; add coverage for new edge cases
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add new /rules route with official league rules covering rosters,
scoring, major-based QP scoring, tiebreakers, draft, and season rules
- Rewrite how-to-play page with a tutorial/marketing tone, highlighting
the Fischer increment draft clock as a novel feature
- Add Rules link to navbar (desktop and mobile)
- Align QP values in both pages with DEFAULT_QP_VALUES in code
- Fix tiebreaker description to match placement-count logic in code
- Update all fantasy point displays from toFixed(1) to toFixed(2) across
StandingsTable, TeamScoreBreakdown, and PointProgressionChart
- Update tests to match new two-decimal-place point display format
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add FIFA_48 bracket template with 12 groups of 4, knockout stage of 32
- Add tournament groups schema, model, and GroupStageDisplay component
- Add projected/expected value scoring to standings and team breakdowns
- Add docker-compose for local PostgreSQL development
- Move DRAFT_ORDER_IMPLEMENTATION.md to plans directory
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Created `TeamScoreBreakdown` component to display all drafted participants, their placements, and points.
- Added new route for team breakdown at `/leagues/:leagueId/standings/:seasonId/teams/:teamId`.
- Enhanced `getTeamScoreBreakdown` model to include `sportsSeasonId` for grouping.
- Updated `StandingsTable` to include clickable team name links.
- Added functionality to finalize brackets in playoff events, including error handling and recalculating standings.
- Implemented tests for `TeamScoreBreakdown` component to ensure proper rendering and functionality.
- Updated routing to support new team breakdown feature and ensure seamless navigation.