Commit graph

6 commits

Author SHA1 Message Date
Chris Parsons
e2b178221a
Add oxlint linting setup with zero errors (#194)
* 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>
2026-03-21 09:44:05 -07:00
Chris Parsons
beab6400b2
Fix partial bracket scoring, rank labels, and Discord notification bugs (#158)
- NCAAM R64/R32 winners no longer receive T5 floor points; only Sweet
  Sixteen winners entering the first scoring round get a floor
- AFL T5-T6 and T7-T8 score as separate tiers (avg([5,6]) and avg([7,8]))
  instead of one shared avg([5-8]) pool
- Eliminated teams now always show their correct final rank (e.g. T33 for
  NCAAM R64 losers) even during partial round scoring, by deriving the
  rank label from the total match count per round rather than the dynamic
  still-alive count
- Discord notifications now fire when a drafted participant is eliminated
  with 0 points (e.g. R64 losers whose team score doesn't change)
- Discord notifications no longer include all prior matches — scoped to
  the current batch via matchIds
- Discord section headers have spacing; rank medals replaced with numbers
- Rounds auto-complete when all matches are marked done; manual Complete
  Round dropdown removed
- Fixed double Discord notification when the last match in a round
  triggers auto-complete: processPlayoffEvent now accepts skipRecalculate
  so the caller controls when the notification fires
- Fixed falsy check on finalPosition=0 in calculateTeamScore
- Undrafted 0-point participants filtered from Eliminated Teams display

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 12:34:10 -07:00
Chris Parsons
dbffddc9ff
Partial bracket scoring, code review fixes, and double-chance logic (#156)
## Partial bracket scoring
- `processMatchResult`: new exported function that scores a single match
  immediately (loser → final placement, winner → provisional floor).
  Called from `set-winner` and `set-round-winners` so points are awarded
  as soon as a winner is set, before the full round is complete.
- `set-winner`: passes `eventName` to `processMatchResult`.
- `set-round-winners`: batches side effects — calls `recalculateAffectedLeagues`
  and `updateProbabilitiesAfterResult` once after the loop instead of per-match
  (`skipSideEffects: true` per match).

## Code review fixes
- **ROUND_CONFIG** (S1): extracted a `RoundScoringConfig` lookup table +
  `getRoundConfig()` helper, eliminating three parallel `if/else` chains in
  `processPlayoffEvent`, `processMatchResult`, and `getGuaranteedMinimumPosition`.
  AFL template overrides live in `TEMPLATE_ROUND_CONFIG` with an explanatory
  comment about why the `bracketTemplateId` guard is required.
- **skipSideEffects** (C2): new param on `processMatchResult`; bracket server
  uses it to batch standings/probability recalc in `set-round-winners`.
- **eventName** (W1): passed through `processMatchResult` → `recalculateAffectedLeagues`.
- **Round validation** (W2): `set-round-winners` now guards `match.round === round`
  before processing each assignment.
- **Comments** (C3/S3/W3): added notes on non-scoring loser assumption,
  `isScoring ?? true` default, and AFL Semi-Finals template requirement.

## PlayoffBracket eliminated-teams fix + tests
- Fixed `computeEliminatedByRound` to track participant *appearances* (not just
  wins), so AFL QF losers who advance to Semi-Finals via double-chance are
  correctly excluded from the QF eliminated list.
- Extracted the logic as an exported pure function for testability.
- Added 4 tests: standard elimination, AFL QF loser → SF win, AFL QF loser →
  SF loss, and normal advancement not protecting a later loser.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 10:50:30 -07:00
Chris Parsons
cf8ac8a765
feat: progressive floor scoring for playoff brackets (#100)
When a participant wins a bracket round, they immediately earn provisional
"floor" points (the averaged minimum they'd receive if eliminated next round).
These update as they advance and are replaced by finalized scores on elimination.

Key changes:
- Add `is_partial_score` column to `participant_results` (migration 0038)
- `processPlayoffEvent`: assign provisional position 5 to non-scoring round
  winners; assign round-appropriate floors to scoring round winners via
  `getGuaranteedMinimumPosition`; add catch-all for unrecognized round names
- `upsertParticipantResult`: guard against un-finalizing rows (never overwrite
  isPartialScore=false with true)
- `calculateBracketPoints`: new function averaging tied bracket tiers
  (5-8 → 20 pts, 3-4 → avg, 1-2 solo); used in `calculateTeamScore` for
  playoff_bracket pattern (pattern-aware, doesn't affect F1/golf scoring)
- `PlayoffBracket`: "In Contention" table for still-active participants;
  AFL double-chance fix (participants who won a later match excluded from
  earlier round's loser list); correct `nextRank` starting position
- Server loader: batch owner DB queries (one query vs N+1); deduplicate
  participantPoints; use calculateBracketPoints for bracket point display
- Clean up Phase/Q-number tracking comments throughout scoring-calculator.ts
- 3 new tests for non-scoring round provisional floor behavior

Also includes a dev admin bypass via DEV_ADMIN_CLERK_ID env var (separate
change on this branch, not part of floor scoring feature).

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 10:27:58 -07:00
Chris Parsons
d88be08deb
User/chris/bracket UI redesign (#95)
* feat: redesign playoff bracket UI with compact layout and owner display

- Replace per-match Card wrappers with compact left-vs-right rows (stacks on mobile)
- Add TeamOwnerBadge component (colored avatar + team name + username), matching the standings "Drafted By" style
- Show owner info below each participant name in bracket matches
- Add TBD forward-reference labels ("Winner of Quarterfinals M1") computed via buildFeederMap
- Add Final Rankings table below bracket showing all participants ranked by elimination round
- Fix round ordering in loader: sort by match count descending (more matches = earlier round)
- Add loser relation to playoff matches query for elimination tracking
- Extract getAvatarColor to app/lib/color-hash.ts (shared across GroupStageDisplay, SeasonStandings, TeamOwnerBadge)
- Extract groupMatchesByRound helper; share grouped map between feeder computation and render
- Remove dead getTeamAvatar from SeasonStandings after TeamOwnerBadge adoption
- Add tests for groupMatchesByRound and buildFeederMap (7 tests)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add draft order card to league home page

Show draft order on the league home page when order is set and season
is in pre_draft or draft status. Includes team name, owner name, and
a link to the draft room.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: enhance playoff bracket with eliminated teams, points, and ownership highlights

- Show eliminated teams (including group-stage losers) in Final Rankings card
- Display computed fantasy points per participant in rankings table
- Card title switches between "Eliminated Teams" and "Final Rankings" based on bracket completion
- Highlight owned participants with electric blue name, dot indicator, and card border in bracket matches
- Highlight owned rows with electric border/background in rankings table
- Suppress EventSchedule for playoff_bracket sports seasons
- Fix title redundancy: bracket section title no longer repeats sport name
- Two-column match grid on desktop (md:grid-cols-2)
- Code review fixes: parallel DB fetches, targeted query for pre-eliminated, single-pass parseFloat, remove IIFE

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 22:44:33 -07:00
Chris Parsons
604eb6980c feat: Add routes and components for sports season display, including playoff brackets and standings 2025-11-12 23:44:33 -08:00