From 7294084c13a0207d4c8ceb540efbfb409ef5ec34 Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Fri, 20 Feb 2026 21:26:27 -0800 Subject: [PATCH] Refactor draft grid sports calculation and improve type safety (#16) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix flex pick count using unique sports drafted, not total season sports The flex count was calculated as `totalPicks - totalSportsInSeason`, which always returned 0 when a team drafted multiple participants from the same sport (e.g. 2 picks from UEFA Champions League with 2 sports in season = 2 - 2 = 0). Now uses the number of unique sports the team has actually drafted from, so 2 picks in 1 sport correctly shows 1 flex used. https://claude.ai/code/session_01DZ6jqgZTDEmJucanBtRCtM * Code review fixes for TeamsDraftedGrid and draft route - Fix misleading early-return message: "No picks have been made yet" only fires when there are no sports configured, so update to say "No sports have been configured for this season." - Remove unused draftOrder and team.seasonId from TeamsDraftedGrid props - Replace availableParticipants prop with sports prop to eliminate duplicate sports derivation; route already computes seasonSportsData - Add alphabetical sort to seasonSportsData so ordering is consistent - Fix availableParticipants: any[] in loader — restructure to ternary so TypeScript infers the Drizzle select type directly - Remove any annotation from seasonSportsData forEach callback (now typed) - Fix teamSportPicks.indexOf(pick) O(n) re-scan — use map callback index i https://claude.ai/code/session_01DZ6jqgZTDEmJucanBtRCtM --------- Co-authored-by: Claude --- app/components/draft/TeamsDraftedGrid.tsx | 37 +++-------- .../leagues/$leagueId.draft.$seasonId.tsx | 63 +++++++++---------- 2 files changed, 39 insertions(+), 61 deletions(-) diff --git a/app/components/draft/TeamsDraftedGrid.tsx b/app/components/draft/TeamsDraftedGrid.tsx index 3781403..c699569 100644 --- a/app/components/draft/TeamsDraftedGrid.tsx +++ b/app/components/draft/TeamsDraftedGrid.tsx @@ -4,11 +4,9 @@ import { useMemo } from "react"; interface TeamsDraftedGridProps { draftSlots: Array<{ id: string; - draftOrder: number; team: { id: string; name: string; - seasonId: string; }; }>; picks: Array<{ @@ -26,13 +24,9 @@ interface TeamsDraftedGridProps { name: string; }; }>; - availableParticipants: Array<{ + sports: Array<{ id: string; name: string; - sport: { - id: string; - name: string; - }; }>; season: { numFlexPicks: number; @@ -42,25 +36,9 @@ interface TeamsDraftedGridProps { export function TeamsDraftedGrid({ draftSlots, picks, - availableParticipants, + sports, season, }: TeamsDraftedGridProps) { - // Get unique sports from all available participants (not just picked ones) - const sports = useMemo(() => { - const sportMap = new Map(); - availableParticipants.forEach((participant) => { - if (!sportMap.has(participant.sport.id)) { - sportMap.set(participant.sport.id, { - id: participant.sport.id, - name: participant.sport.name, - }); - } - }); - return Array.from(sportMap.values()).sort((a, b) => - a.name.localeCompare(b.name) - ); - }, [availableParticipants]); - // Calculate picks by team and sport const picksByTeamAndSport = useMemo(() => { const map = new Map>(); @@ -85,17 +63,18 @@ export function TeamsDraftedGrid({ draftSlots.forEach((slot) => { const teamPicks = picks.filter((p) => p.team.id === slot.team.id); - const flexUsed = Math.max(0, teamPicks.length - sports.length); + const uniqueSportsPicked = new Set(teamPicks.map((p) => p.sport.id)).size; + const flexUsed = Math.max(0, teamPicks.length - uniqueSportsPicked); map.set(slot.team.id, flexUsed); }); return map; - }, [picks, draftSlots, sports]); + }, [picks, draftSlots]); if (sports.length === 0) { return (
-

No picks have been made yet

+

No sports have been configured for this season.

); } @@ -156,7 +135,7 @@ export function TeamsDraftedGrid({ > {teamSportPicks.length > 0 ? (
- {teamSportPicks.map((pick) => ( + {teamSportPicks.map((pick, i) => (
- {teamSportPicks.indexOf(pick) + 1} + {i + 1} )}
diff --git a/app/routes/leagues/$leagueId.draft.$seasonId.tsx b/app/routes/leagues/$leagueId.draft.$seasonId.tsx index 480c5b5..1381130 100644 --- a/app/routes/leagues/$leagueId.draft.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft.$seasonId.tsx @@ -119,35 +119,32 @@ export async function loader(args: any) { // Get ALL participants (both drafted and undrafted) - sorted by EV desc, then name // Filtering will be done on the client side based on the "Show Drafted" toggle - let availableParticipants: any[] = []; - - if (sportsSeasonIds.length > 0) { - availableParticipants = await db - .select({ - id: schema.participants.id, - name: schema.participants.name, - sport: schema.sports, - }) - .from(schema.participants) - .innerJoin( - schema.sportsSeasons, - eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id) - ) - .innerJoin( - schema.sports, - eq(schema.sportsSeasons.sportId, schema.sports.id) - ) - .where( - sportsSeasonIds.length === 1 - ? eq(schema.participants.sportsSeasonId, sportsSeasonIds[0]) - : inArray(schema.participants.sportsSeasonId, sportsSeasonIds) - ) - .orderBy( - desc(schema.participants.expectedValue), - asc(schema.participants.name) - ); - - } + const availableParticipants = sportsSeasonIds.length > 0 + ? await db + .select({ + id: schema.participants.id, + name: schema.participants.name, + sport: schema.sports, + }) + .from(schema.participants) + .innerJoin( + schema.sportsSeasons, + eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id) + ) + .innerJoin( + schema.sports, + eq(schema.sportsSeasons.sportId, schema.sports.id) + ) + .where( + sportsSeasonIds.length === 1 + ? eq(schema.participants.sportsSeasonId, sportsSeasonIds[0]) + : inArray(schema.participants.sportsSeasonId, sportsSeasonIds) + ) + .orderBy( + desc(schema.participants.expectedValue), + asc(schema.participants.name) + ) + : []; // Load user's team queue if they have a team const userQueue = userTeam ? await getTeamQueue(userTeam.id) : []; @@ -305,12 +302,14 @@ export default function DraftRoom() { const seasonSportsData = useMemo(() => { const sportsMap = new Map(); - availableParticipants.forEach((p: any) => { + availableParticipants.forEach((p) => { if (!sportsMap.has(p.sport.id)) { sportsMap.set(p.sport.id, { id: p.sport.id, name: p.sport.name }); } }); - return Array.from(sportsMap.values()); + return Array.from(sportsMap.values()).sort((a, b) => + a.name.localeCompare(b.name) + ); }, [availableParticipants]); // Calculate draft eligibility for current user's team @@ -979,7 +978,7 @@ export default function DraftRoom() {