diff --git a/app/draft/[id]/draft-room.tsx b/app/draft/[id]/draft-room.tsx index 4828249..6a30ea7 100644 --- a/app/draft/[id]/draft-room.tsx +++ b/app/draft/[id]/draft-room.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useState, useTransition } from 'react'; +import { useEffect, useState, useMemo, useTransition } from 'react'; import { createClient } from '@/app/utils/supabase/client'; import DraftQueue from './draft-queue'; import PlayerSearch from './player-search'; @@ -10,15 +10,21 @@ import DraftInfoPanel from './draft-info-panel'; import { makePick, startDraft, togglePause, toggleAutoPick, triggerAutoPick } from './actions'; import { Card, CardContent } from "@/components/ui/card"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; +import { getDefaultExpectedGames, parsePlayerSummary, computeProjectedTotal } from '@/app/rankings/ncaa/utils'; + +type PlayerSortBy = 'rank' | 'projection' | 'name'; interface DraftRoomProps { draftSettings: any; currentTeam: any; isCommissioner: boolean; leagueTeams?: any[]; + userRankings?: any[]; + userTeamSettings?: any[]; + ncaaTeamInfo?: any[]; } -export default function DraftRoom({ draftSettings, currentTeam, isCommissioner, leagueTeams = [] }: DraftRoomProps) { +export default function DraftRoom({ draftSettings, currentTeam, isCommissioner, leagueTeams = [], userRankings = [], userTeamSettings = [], ncaaTeamInfo = [] }: DraftRoomProps) { const supabase = createClient(); // Draft state @@ -33,6 +39,63 @@ export default function DraftRoom({ draftSettings, currentTeam, isCommissioner, const [isPaused, setIsPaused] = useState(draftSettings.is_paused || false); const [pickError, setPickError] = useState(null); const [isPending, startTransition] = useTransition(); + const [isQueueOpen, setIsQueueOpen] = useState(true); + + const isNcaam = draftSettings.leagues.league === 'NCAAM'; + const [playerSortBy, setPlayerSortBy] = useState(isNcaam ? 'rank' : 'name'); + + // Build NCAAM ranking/projection maps + const expectedGamesMap = useMemo(() => { + if (!isNcaam) return {}; + const map: Record = {}; + for (const ti of ncaaTeamInfo) { + map[ti.team_name] = getDefaultExpectedGames(ti.seed); + } + for (const s of userTeamSettings) { + map[s.team_name] = s.expected_games; + } + return map; + }, [isNcaam, ncaaTeamInfo, userTeamSettings]); + + const rankMap = useMemo(() => { + const m = new Map(); + for (const r of userRankings) { + m.set(r.player_id, r.rank_position); + } + return m; + }, [userRankings]); + + // Enrich players with NCAAM rank/projection and sort + const enrichAndSort = (players: any[]): any[] => { + if (!isNcaam) return players; + return players.map(p => { + const { ppg } = parsePlayerSummary(p.summary); + const expGames = expectedGamesMap[p.team_name] || 1; + return { + ...p, + rank: rankMap.get(p.id) ?? null, + projectedTotal: computeProjectedTotal(ppg, expGames), + }; + }); + }; + + const sortPlayers = (players: any[], sort: PlayerSortBy): any[] => { + const sorted = [...players]; + if (sort === 'rank') { + sorted.sort((a, b) => { + const aRank = a.rank; + const bRank = b.rank; + if (aRank != null && bRank != null) return aRank - bRank; + if (aRank != null) return -1; + if (bRank != null) return 1; + return (b.projectedTotal || 0) - (a.projectedTotal || 0); + }); + } else if (sort === 'projection') { + sorted.sort((a, b) => (b.projectedTotal || 0) - (a.projectedTotal || 0)); + } + // 'name' keeps the original alphabetical order from the query + return sorted; + }; // Get current pick team information useEffect(() => { @@ -149,8 +212,10 @@ export default function DraftRoom({ draftSettings, currentTeam, isCommissioner, if (playersError) throw playersError; - setAvailablePlayers(players || []); - setSearchResults(players || []); + const enriched = enrichAndSort(players || []); + const sorted = sortPlayers(enriched, playerSortBy); + setAvailablePlayers(sorted); + setSearchResults(sorted); } catch (error) { console.error('Error fetching available players:', error); } finally { @@ -274,6 +339,13 @@ export default function DraftRoom({ draftSettings, currentTeam, isCommissioner, setSearchResults(filtered); }; + const handleSortChange = (sort: PlayerSortBy) => { + setPlayerSortBy(sort); + const sorted = sortPlayers(availablePlayers, sort); + setAvailablePlayers(sorted); + setSearchResults(sorted); + }; + const isDraftActive = draftState.draft_status === 'in_progress'; const isDraftCompleted = draftState.draft_status === 'completed'; const isMyTurn = currentPickTeam?.id === currentTeam.id && isDraftActive; @@ -349,7 +421,7 @@ export default function DraftRoom({ draftSettings, currentTeam, isCommissioner,
{/* Player Search - main area */} -
+

Available Players

@@ -403,20 +475,48 @@ export default function DraftRoom({ draftSettings, currentTeam, isCommissioner, isDraftActive={isDraftActive} selectedPlayer={selectedPlayer} leagueType={draftSettings.leagues.league} + isNcaam={isNcaam} + sortBy={playerSortBy} + onSortChange={handleSortChange} />
- {/* Draft Queue */} -
- - -

Your Draft Queue

- -
-
-
+ {/* Draft Queue - collapsible */} + {isQueueOpen ? ( +
+ + +
+

Your Draft Queue

+ +
+ +
+
+
+ ) : ( +
+ +
+ )} {/* Draft Info Panel */}
diff --git a/app/draft/[id]/page.tsx b/app/draft/[id]/page.tsx index 2953285..61e176c 100644 --- a/app/draft/[id]/page.tsx +++ b/app/draft/[id]/page.tsx @@ -75,12 +75,40 @@ export default async function DraftPage({ return
Error loading teams
; } - const currentTeam = userTeam || { + const currentTeam = userTeam || { id: 'commissioner', name: 'Commissioner View', league_id: draftSettings.league_id }; + // Fetch NCAA rankings data if this is an NCAAM league + let userRankings: any[] = []; + let userTeamSettings: any[] = []; + let ncaaTeamInfo: any[] = []; + + if (draftSettings.leagues.league === 'NCAAM') { + const [rankingsRes, teamSettingsRes, teamInfoRes] = await Promise.all([ + supabase + .from('user_ncaa_rankings') + .select('*') + .eq('user_id', user.id) + .eq('season_year', 2026), + supabase + .from('user_ncaa_team_settings') + .select('*') + .eq('user_id', user.id) + .eq('season_year', 2026), + supabase + .from('ncaa_team_info') + .select('*') + .eq('season_year', 2026), + ]); + + userRankings = rankingsRes.data || []; + userTeamSettings = teamSettingsRes.data || []; + ncaaTeamInfo = teamInfoRes.data || []; + } + return (
@@ -91,11 +119,14 @@ export default async function DraftPage({
-
diff --git a/app/draft/[id]/picks-carousel.tsx b/app/draft/[id]/picks-carousel.tsx index b6c7ff8..bfe22a9 100644 --- a/app/draft/[id]/picks-carousel.tsx +++ b/app/draft/[id]/picks-carousel.tsx @@ -1,7 +1,7 @@ 'use client'; import { useRef, useEffect, useMemo } from 'react'; -import { getPositionColor, getPositionBorderColor, computePickInfo, getTeamIdForPick } from './utils'; +import { getPositionColor, getPositionBorderColor, getPositionsForLeague, computePickInfo, getTeamIdForPick } from './utils'; interface PicksCarouselProps { leagueTeams: any[]; @@ -52,6 +52,41 @@ export default function PicksCarousel({ return map; }, [leagueTeams]); + const positions = getPositionsForLeague(leagueType); + + // Build roster counts per team: teamId -> { G: 2, F: 1, ... } + const teamRosters = useMemo(() => { + const rosters: Record> = {}; + for (const pick of draftPicks) { + const teamId = pick.team?.id || pick.team_id; + const position = pick.player?.position; + if (teamId && position) { + if (!rosters[teamId]) rosters[teamId] = {}; + rosters[teamId][position] = (rosters[teamId][position] || 0) + 1; + } + } + return rosters; + }, [draftPicks]); + + // Cumulative roster at each pick number: pickNumber -> { G: x, F: y, ... } + const rosterAtPick = useMemo(() => { + const map: Record> = {}; + const running: Record> = {}; + // Sort picks by pick_number to build cumulative state + const sorted = [...draftPicks].sort((a, b) => (a.pick_number || 0) - (b.pick_number || 0)); + for (const pick of sorted) { + const teamId = pick.team?.id || pick.team_id; + const position = pick.player?.position; + if (teamId && position && pick.pick_number != null) { + if (!running[teamId]) running[teamId] = {}; + running[teamId][position] = (running[teamId][position] || 0) + 1; + // Snapshot the team's roster state at this pick + map[pick.pick_number] = { ...running[teamId] }; + } + } + return map; + }, [draftPicks]); + const currentOverallPick = (currentRound - 1) * totalTeams + currentPick; // Auto-scroll to current pick @@ -109,6 +144,7 @@ export default function PicksCarousel({ const playerTeam = pick.player?.team || ''; const posColor = getPositionColor(position, leagueType); const borderColor = getPositionBorderColor(position, leagueType); + const roster = rosterAtPick[overallPick] || {}; return (
{/* Team name */}

{teamName}

+ {/* Roster breakdown */} +
+ {positions.map((pos) => { + const count = roster[pos] || 0; + if (count === 0) return null; + return ( + + {pos}{count} + + ); + })} +
); } - // Empty / future pick + // Empty / future pick — show current team roster + const currentRoster = teamRosters[teamId] || {}; + const hasRoster = Object.values(currentRoster).some(c => c > 0); + return (

{notation}

{teamName}

+ {hasRoster && ( +
+ {positions.map((pos) => { + const count = currentRoster[pos] || 0; + if (count === 0) return null; + return ( + + {pos}{count} + + ); + })} +
+ )}
); })} diff --git a/app/draft/[id]/player-search.tsx b/app/draft/[id]/player-search.tsx index cf867ad..1c9a5ed 100644 --- a/app/draft/[id]/player-search.tsx +++ b/app/draft/[id]/player-search.tsx @@ -16,6 +16,8 @@ import { TableRow, } from "@/components/ui/table"; +type PlayerSortBy = 'rank' | 'projection' | 'name'; + interface PlayerSearchProps { availablePlayers: any[]; searchResults: any[]; @@ -28,6 +30,9 @@ interface PlayerSearchProps { isDraftActive?: boolean; selectedPlayer: any | null; leagueType?: string; + isNcaam?: boolean; + sortBy?: PlayerSortBy; + onSortChange?: (sortBy: PlayerSortBy) => void; } export default function PlayerSearch({ @@ -41,7 +46,10 @@ export default function PlayerSearch({ isCommissioner = false, isDraftActive = false, selectedPlayer, - leagueType = 'NFL' + leagueType = 'NFL', + isNcaam = false, + sortBy = 'name', + onSortChange, }: PlayerSearchProps) { const [searchTerm, setSearchTerm] = useState(''); const [currentPosition, setCurrentPosition] = useState('All'); @@ -94,6 +102,29 @@ export default function PlayerSearch({ ))} + + {isNcaam && onSortChange && ( +
+ Sort: + {(['rank', 'projection'] as const).map((key) => { + const label = key === 'rank' ? 'Rank' : 'Projection'; + const isActive = sortBy === key; + return ( + + ); + })} +
+ )}
@@ -110,11 +141,18 @@ export default function PlayerSearch({ - Player + Player Pos Team - Summary - Action + {isNcaam ? ( + <> + Rank + Proj + + ) : ( + Summary + )} + Action @@ -138,7 +176,22 @@ export default function PlayerSearch({ {player.position} {player.team_name} - {player.summary || '-'} + {isNcaam ? ( + <> + + {player.rank != null ? ( + {player.rank} + ) : ( + - + )} + + + {player.projectedTotal ?? '-'} + + + ) : ( + {player.summary || '-'} + )}