brackt/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.tsx

196 lines
7 KiB
TypeScript
Raw Normal View History

import { Link } from "react-router";
import type { Route } from "./+types/$leagueId.sports-seasons.$sportsSeasonId";
import { loader } from "./$leagueId.sports-seasons.$sportsSeasonId.server";
import { SportSeasonDisplay } from "~/components/scoring/SportSeasonDisplay";
import { EventSchedule } from "~/components/sport-season/EventSchedule";
import { RegularSeasonStandings } from "~/components/sport-season/RegularSeasonStandings";
Add FIFA World Cup 2026 support with group stage display and Monte Carlo simulator (#242) * Add FIFA World Cup 2026 support with group stage display and Monte Carlo simulator, fixes #127 - New `groupStageMatches` table for recording group play results (W/D/L, scores, matchday, schedule) - `computeGroupStandings()` model function: pts → GD → GF → name tiebreaker ordering - `GroupStageStandings` component showing all 12 groups with standings table and manager column - Admin bracket UI: group match score entry, per-group standings, "Recalculate Floors" action - `WorldCupSimulator`: 50k Monte Carlo covering group stage + best-8 3rd-place + knockout + 3rd place game - Fuzzy name matching for national team Elo lookup (exact → substring → word-overlap), warns on miss - Partial group completion: completed matches replayed with real scores, remaining matches simulated - Elo priority: admin-entered sourceElo > futures odds converted to Elo > hardcoded national team ratings - `fifa_48` bracket template: added Third Place Game round with `loserFeedsInto` on Semifinals - Scoring rules: distinct 3rd/4th place for `fifa_48` (not averaged), QF losers share 5th–8th equally - Floor scoring: SF participants guaranteed 4th (provisional), finalized after 3rd place game - `recalculate-floors` admin action deletes and replays all results from scratch (fixes stale guard bug) - Unique index on `(tournamentGroupId, participant1Id, participant2Id)` to prevent duplicate pairings - Batch `findMatchesByGroupIds()` replacing N sequential queries in the sport season loader - League home mini-standings now shows `actualPoints` (includes floor) instead of `totalPoints` only - Elo ratings admin page supports World Cup (same bulk-import flow as snooker) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix TypeScript errors: update GroupStandingData type to use findMatchesByGroupIds Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Increase Node heap to 4GB for unit tests in CI to prevent OOM Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix OOM in CI: make WorldCupSimulator simulation count configurable for tests Tests now pass numSimulations=500 instead of the production default of 50,000. Six simulator tests × 50k iterations each was exhausting the 4GB heap on GitHub Actions runners. Also reduce simGroupMatch stat tests from 50k to 5k iterations. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 10:27:47 -07:00
import { GroupStageStandings } from "~/components/sport-season/GroupStageStandings";
import { Button } from "~/components/ui/button";
import { Badge } from "~/components/ui/badge";
import { ArrowLeft, CheckCircle2, Clock, Zap } from "lucide-react";
export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors {
return [{ title: `${data?.sportsSeason?.name ?? "Sport Season"}${data?.league?.name ?? "League"} - Brackt` }];
}
export { loader };
function getStatusBadge(status: string) {
switch (status) {
case "active":
return (
<Badge variant="outline" className="bg-emerald-500/15 text-emerald-400 border-emerald-500/30">
<Zap className="mr-1 h-3 w-3" />
Active
</Badge>
);
case "upcoming":
return (
<Badge variant="outline" className="bg-electric/15 text-electric border-electric/30">
<Clock className="mr-1 h-3 w-3" />
Upcoming
</Badge>
);
case "completed":
return (
<Badge variant="outline" className="bg-muted text-muted-foreground border-border">
<CheckCircle2 className="mr-1 h-3 w-3" />
Completed
</Badge>
);
default:
return null;
}
}
export default function SportSeasonDetail({
loaderData,
}: Route.ComponentProps) {
const {
league,
season,
sportsSeason,
scoringPattern,
playoffMatches,
playoffRounds,
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
preEliminatedParticipants,
participantPoints,
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
partialScoreParticipantIds,
seasonStandings,
qpStandings,
teamOwnerships,
userParticipantIds,
upcomingEvents,
recentEvents,
seasonIsFinalized,
regularSeasonStandings,
participantEvs,
Add FIFA World Cup 2026 support with group stage display and Monte Carlo simulator (#242) * Add FIFA World Cup 2026 support with group stage display and Monte Carlo simulator, fixes #127 - New `groupStageMatches` table for recording group play results (W/D/L, scores, matchday, schedule) - `computeGroupStandings()` model function: pts → GD → GF → name tiebreaker ordering - `GroupStageStandings` component showing all 12 groups with standings table and manager column - Admin bracket UI: group match score entry, per-group standings, "Recalculate Floors" action - `WorldCupSimulator`: 50k Monte Carlo covering group stage + best-8 3rd-place + knockout + 3rd place game - Fuzzy name matching for national team Elo lookup (exact → substring → word-overlap), warns on miss - Partial group completion: completed matches replayed with real scores, remaining matches simulated - Elo priority: admin-entered sourceElo > futures odds converted to Elo > hardcoded national team ratings - `fifa_48` bracket template: added Third Place Game round with `loserFeedsInto` on Semifinals - Scoring rules: distinct 3rd/4th place for `fifa_48` (not averaged), QF losers share 5th–8th equally - Floor scoring: SF participants guaranteed 4th (provisional), finalized after 3rd place game - `recalculate-floors` admin action deletes and replays all results from scratch (fixes stale guard bug) - Unique index on `(tournamentGroupId, participant1Id, participant2Id)` to prevent duplicate pairings - Batch `findMatchesByGroupIds()` replacing N sequential queries in the sport season loader - League home mini-standings now shows `actualPoints` (includes floor) instead of `totalPoints` only - Elo ratings admin page supports World Cup (same bulk-import flow as snooker) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix TypeScript errors: update GroupStandingData type to use findMatchesByGroupIds Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Increase Node heap to 4GB for unit tests in CI to prevent OOM Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix OOM in CI: make WorldCupSimulator simulation count configurable for tests Tests now pass numSimulations=500 instead of the production default of 50,000. Six simulator tests × 50k iterations each was exhausting the 4GB heap on GitHub Actions runners. Also reduce simGroupMatch stat tests from 50k to 5k iterations. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 10:27:47 -07:00
groupStandings,
} = loaderData;
const hasBracket = playoffMatches && playoffMatches.length > 0;
const hasStandings = regularSeasonStandings && regularSeasonStandings.length > 0;
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
const showOtLosses = hasStandings && regularSeasonStandings.some((s) => s.otLosses !== null);
const simulatorType = sportsSeason.sport?.simulatorType;
const standingsDisplayMode =
Add MLB playoff simulator with AL/NL division standings, fixes #121 (#225) * Add MLB playoff simulator with AL/NL division standings, fixes #121 - New `mlb-simulator.ts`: 50k Monte Carlo sim drawing division winners (weighted by p_div) and 3 WC teams per league, then simulating WC best-of-3 → DS best-of-5 → LCS best-of-7 → World Series. Elo parity factor 350; blends Vegas odds 30/70 when sourceOdds available. - New `standings-sync/mlb.ts`: adapter for free statsapi.mlb.com API, mapping AL/NL conferences, division ranks, streaks, home/away/L10 splits. - New `mlb-divisions` display mode in RegularSeasonStandings: shows 1 division winner per division section + Wild Card section with 3-spot playoff line, headings read "AL/NL League" instead of "Conference". - Refactored buildNhlSections/buildMlbSections into shared buildDivisionSections (divisionSpots + wcSpots params); conferenceLabel now flows through group objects rather than being derived from displayMode in the render. - DB migration 0062 adds `mlb_bracket` to simulator_type enum. - 37 new tests across simulator, adapter, and component. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix flaky tennis simulator test: increase trials and lower threshold 500 trials with a ~3–5% expected win rate had enough variance to occasionally land below the 0.03 threshold (~2% failure rate). Bumping to 2000 trials reduces the std dev by 2x; lowering the threshold to 0.02 keeps the assertion meaningful (still well above random 0.78%) while eliminating the flakiness. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 08:47:02 -07:00
simulatorType === "nhl_bracket" ? "nhl-divisions" :
simulatorType === "mlb_bracket" ? "mlb-divisions" :
"flat";
// NBA: 10 teams make the postseason (6 auto-qualify + 4 play-in)
// AFL: top 10 advance to the finals series (Wildcard + QF/EF + SF + PF + GF)
Add MLB playoff simulator with AL/NL division standings, fixes #121 (#225) * Add MLB playoff simulator with AL/NL division standings, fixes #121 - New `mlb-simulator.ts`: 50k Monte Carlo sim drawing division winners (weighted by p_div) and 3 WC teams per league, then simulating WC best-of-3 → DS best-of-5 → LCS best-of-7 → World Series. Elo parity factor 350; blends Vegas odds 30/70 when sourceOdds available. - New `standings-sync/mlb.ts`: adapter for free statsapi.mlb.com API, mapping AL/NL conferences, division ranks, streaks, home/away/L10 splits. - New `mlb-divisions` display mode in RegularSeasonStandings: shows 1 division winner per division section + Wild Card section with 3-spot playoff line, headings read "AL/NL League" instead of "Conference". - Refactored buildNhlSections/buildMlbSections into shared buildDivisionSections (divisionSpots + wcSpots params); conferenceLabel now flows through group objects rather than being derived from displayMode in the render. - DB migration 0062 adds `mlb_bracket` to simulator_type enum. - 37 new tests across simulator, adapter, and component. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix flaky tennis simulator test: increase trials and lower threshold 500 trials with a ~3–5% expected win rate had enough variance to occasionally land below the 0.03 threshold (~2% failure rate). Bumping to 2000 trials reduces the std dev by 2x; lowering the threshold to 0.02 keeps the assertion meaningful (still well above random 0.78%) while eliminating the flakiness. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 08:47:02 -07:00
// NHL: handled by nhl-divisions mode (3 per div + 2 wild cards)
// MLB: handled by mlb-divisions mode (1 per div + 3 wild cards)
const playoffSpots =
simulatorType === "nba_bracket" || simulatorType === "afl_bracket" ? 10 : 8;
// Build ownership map for RegularSeasonStandings
const ownershipMap = Object.fromEntries(
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
teamOwnerships.map((o) => [
o.participantId,
{ teamName: o.teamName, ownerName: o.ownerName ?? "", teamId: o.teamId },
])
);
return (
<div className="container mx-auto py-8 px-4">
<div className="mb-6">
<Button variant="ghost" size="sm" asChild className="mb-4">
<Link to={`/leagues/${league.id}`}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to League
</Link>
</Button>
<div className="flex items-start justify-between gap-4 mb-4">
<div>
<h1 className="text-3xl font-bold mb-1">
{sportsSeason.sport.name}
</h1>
<p className="text-muted-foreground">
{sportsSeason.name} {league.name} {season.year} Season
</p>
</div>
{getStatusBadge(sportsSeason.status)}
</div>
</div>
{/* Regular season standings — show above bracket when no bracket exists yet */}
{hasStandings && !hasBracket && (
<div className="mb-6">
<RegularSeasonStandings
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
standings={regularSeasonStandings}
teamOwnerships={ownershipMap}
userParticipantIds={userParticipantIds}
showOtLosses={showOtLosses}
participantEvs={participantEvs}
Add MLB playoff simulator with AL/NL division standings, fixes #121 (#225) * Add MLB playoff simulator with AL/NL division standings, fixes #121 - New `mlb-simulator.ts`: 50k Monte Carlo sim drawing division winners (weighted by p_div) and 3 WC teams per league, then simulating WC best-of-3 → DS best-of-5 → LCS best-of-7 → World Series. Elo parity factor 350; blends Vegas odds 30/70 when sourceOdds available. - New `standings-sync/mlb.ts`: adapter for free statsapi.mlb.com API, mapping AL/NL conferences, division ranks, streaks, home/away/L10 splits. - New `mlb-divisions` display mode in RegularSeasonStandings: shows 1 division winner per division section + Wild Card section with 3-spot playoff line, headings read "AL/NL League" instead of "Conference". - Refactored buildNhlSections/buildMlbSections into shared buildDivisionSections (divisionSpots + wcSpots params); conferenceLabel now flows through group objects rather than being derived from displayMode in the render. - DB migration 0062 adds `mlb_bracket` to simulator_type enum. - 37 new tests across simulator, adapter, and component. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix flaky tennis simulator test: increase trials and lower threshold 500 trials with a ~3–5% expected win rate had enough variance to occasionally land below the 0.03 threshold (~2% failure rate). Bumping to 2000 trials reduces the std dev by 2x; lowering the threshold to 0.02 keeps the assertion meaningful (still well above random 0.78%) while eliminating the flakiness. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 08:47:02 -07:00
displayMode={standingsDisplayMode as "flat" | "nhl-divisions" | "mlb-divisions"}
playoffSpots={playoffSpots}
/>
</div>
)}
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
{/* Event schedule - hidden for bracket sports (the bracket itself is the event) */}
{scoringPattern !== "playoff_bracket" &&
(upcomingEvents.length > 0 || recentEvents.length > 0) && (
<div className="mb-6">
<EventSchedule
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
upcomingEvents={upcomingEvents}
recentEvents={recentEvents}
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
/>
</div>
)}
Add FIFA World Cup 2026 support with group stage display and Monte Carlo simulator (#242) * Add FIFA World Cup 2026 support with group stage display and Monte Carlo simulator, fixes #127 - New `groupStageMatches` table for recording group play results (W/D/L, scores, matchday, schedule) - `computeGroupStandings()` model function: pts → GD → GF → name tiebreaker ordering - `GroupStageStandings` component showing all 12 groups with standings table and manager column - Admin bracket UI: group match score entry, per-group standings, "Recalculate Floors" action - `WorldCupSimulator`: 50k Monte Carlo covering group stage + best-8 3rd-place + knockout + 3rd place game - Fuzzy name matching for national team Elo lookup (exact → substring → word-overlap), warns on miss - Partial group completion: completed matches replayed with real scores, remaining matches simulated - Elo priority: admin-entered sourceElo > futures odds converted to Elo > hardcoded national team ratings - `fifa_48` bracket template: added Third Place Game round with `loserFeedsInto` on Semifinals - Scoring rules: distinct 3rd/4th place for `fifa_48` (not averaged), QF losers share 5th–8th equally - Floor scoring: SF participants guaranteed 4th (provisional), finalized after 3rd place game - `recalculate-floors` admin action deletes and replays all results from scratch (fixes stale guard bug) - Unique index on `(tournamentGroupId, participant1Id, participant2Id)` to prevent duplicate pairings - Batch `findMatchesByGroupIds()` replacing N sequential queries in the sport season loader - League home mini-standings now shows `actualPoints` (includes floor) instead of `totalPoints` only - Elo ratings admin page supports World Cup (same bulk-import flow as snooker) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix TypeScript errors: update GroupStandingData type to use findMatchesByGroupIds Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Increase Node heap to 4GB for unit tests in CI to prevent OOM Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix OOM in CI: make WorldCupSimulator simulation count configurable for tests Tests now pass numSimulations=500 instead of the production default of 50,000. Six simulator tests × 50k iterations each was exhausting the 4GB heap on GitHub Actions runners. Also reduce simGroupMatch stat tests from 50k to 5k iterations. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 10:27:47 -07:00
{/* Group stage standings — shown for FIFA-style tournaments before/during group phase */}
{groupStandings.length > 0 && (
<div className="mb-6">
<GroupStageStandings groups={groupStandings} ownershipMap={ownershipMap} showEmpty={false} />
</div>
)}
{/* Hide bracket display when standings exist but no bracket matches have been set yet */}
{!(scoringPattern === "playoff_bracket" && hasStandings && !hasBracket) && <SportSeasonDisplay
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
scoringPattern={scoringPattern as "playoff_bracket" | "season_standings" | "qualifying_points"}
sportSeasonName={sportsSeason.name}
sportName={sportsSeason.sport.name}
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
playoffMatches={playoffMatches}
playoffRounds={playoffRounds}
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
preEliminatedParticipants={preEliminatedParticipants}
participantPoints={participantPoints}
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
partialScoreParticipantIds={partialScoreParticipantIds}
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
seasonStandings={seasonStandings}
seasonIsFinalized={seasonIsFinalized}
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
qpStandings={qpStandings}
qpIsFinalized={sportsSeason.qualifyingPointsFinalized || false}
totalMajors={sportsSeason.totalMajors}
majorsCompleted={sportsSeason.majorsCompleted || 0}
canFinalize={
(sportsSeason.majorsCompleted || 0) >=
(sportsSeason.totalMajors || 0) &&
!sportsSeason.qualifyingPointsFinalized
}
teamOwnerships={teamOwnerships}
userParticipantIds={userParticipantIds}
scoringRules={season}
showOwnership={true}
/>}
{/* Regular season standings — show below bracket once bracket exists */}
{hasStandings && hasBracket && (
<div className="mt-8">
<RegularSeasonStandings
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
standings={regularSeasonStandings}
teamOwnerships={ownershipMap}
userParticipantIds={userParticipantIds}
showOtLosses={showOtLosses}
participantEvs={participantEvs}
Add MLB playoff simulator with AL/NL division standings, fixes #121 (#225) * Add MLB playoff simulator with AL/NL division standings, fixes #121 - New `mlb-simulator.ts`: 50k Monte Carlo sim drawing division winners (weighted by p_div) and 3 WC teams per league, then simulating WC best-of-3 → DS best-of-5 → LCS best-of-7 → World Series. Elo parity factor 350; blends Vegas odds 30/70 when sourceOdds available. - New `standings-sync/mlb.ts`: adapter for free statsapi.mlb.com API, mapping AL/NL conferences, division ranks, streaks, home/away/L10 splits. - New `mlb-divisions` display mode in RegularSeasonStandings: shows 1 division winner per division section + Wild Card section with 3-spot playoff line, headings read "AL/NL League" instead of "Conference". - Refactored buildNhlSections/buildMlbSections into shared buildDivisionSections (divisionSpots + wcSpots params); conferenceLabel now flows through group objects rather than being derived from displayMode in the render. - DB migration 0062 adds `mlb_bracket` to simulator_type enum. - 37 new tests across simulator, adapter, and component. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix flaky tennis simulator test: increase trials and lower threshold 500 trials with a ~3–5% expected win rate had enough variance to occasionally land below the 0.03 threshold (~2% failure rate). Bumping to 2000 trials reduces the std dev by 2x; lowering the threshold to 0.02 keeps the assertion meaningful (still well above random 0.78%) while eliminating the flakiness. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 08:47:02 -07:00
displayMode={standingsDisplayMode as "flat" | "nhl-divisions" | "mlb-divisions"}
playoffSpots={playoffSpots}
/>
</div>
)}
</div>
);
}