2025-10-16 01:12:50 -07:00
|
|
|
import { useMemo } from "react";
|
2025-10-16 01:36:14 -07:00
|
|
|
import { DraftTimer } from "./DraftTimer";
|
2025-10-16 01:12:50 -07:00
|
|
|
|
|
|
|
|
interface Team {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
draftOrder: number;
|
|
|
|
|
ownerId: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface DraftPick {
|
|
|
|
|
id: string;
|
|
|
|
|
teamId: string;
|
|
|
|
|
participantId: string;
|
|
|
|
|
pickNumber: number;
|
|
|
|
|
round: number;
|
|
|
|
|
pickInRound: number;
|
|
|
|
|
pickedByType: "owner" | "commissioner" | "auto";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Participant {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
sport: string;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 01:36:14 -07:00
|
|
|
interface Timer {
|
|
|
|
|
teamId: string;
|
|
|
|
|
timeRemaining: number;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 01:12:50 -07:00
|
|
|
interface DraftGridProps {
|
|
|
|
|
teams: Team[];
|
|
|
|
|
draftPicks: DraftPick[];
|
|
|
|
|
participants: Participant[];
|
|
|
|
|
draftRounds: number;
|
|
|
|
|
currentPickNumber: number;
|
|
|
|
|
userTeamId?: string;
|
2025-10-16 01:36:14 -07:00
|
|
|
timers?: Timer[];
|
2025-10-16 01:12:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function DraftGrid({
|
|
|
|
|
teams,
|
|
|
|
|
draftPicks,
|
|
|
|
|
participants,
|
|
|
|
|
draftRounds,
|
|
|
|
|
currentPickNumber,
|
|
|
|
|
userTeamId,
|
2025-10-16 01:36:14 -07:00
|
|
|
timers = [],
|
2025-10-16 01:12:50 -07:00
|
|
|
}: DraftGridProps) {
|
|
|
|
|
// Create a map of participant IDs to participant data for quick lookup
|
|
|
|
|
const participantMap = useMemo(() => {
|
|
|
|
|
return new Map(participants.map((p) => [p.id, p]));
|
|
|
|
|
}, [participants]);
|
|
|
|
|
|
|
|
|
|
// Create a map of pick numbers to draft picks
|
|
|
|
|
const pickMap = useMemo(() => {
|
|
|
|
|
return new Map(draftPicks.map((p) => [p.pickNumber, p]));
|
|
|
|
|
}, [draftPicks]);
|
|
|
|
|
|
2025-10-16 01:36:14 -07:00
|
|
|
// Create a map of team IDs to timers
|
|
|
|
|
const timerMap = useMemo(() => {
|
|
|
|
|
return new Map(timers.map((t) => [t.teamId, t]));
|
|
|
|
|
}, [timers]);
|
|
|
|
|
|
2025-10-16 01:12:50 -07:00
|
|
|
// Calculate pick number for a given round and team
|
|
|
|
|
const getPickNumber = (round: number, teamIndex: number): number => {
|
|
|
|
|
const isOddRound = round % 2 === 1;
|
|
|
|
|
const pickInRound = isOddRound ? teamIndex + 1 : teams.length - teamIndex;
|
|
|
|
|
return (round - 1) * teams.length + pickInRound;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="overflow-x-auto">
|
|
|
|
|
<div className="inline-block min-w-full">
|
|
|
|
|
{/* Header Row - Team Names */}
|
|
|
|
|
<div className="flex border-b-2 border-border bg-muted/50 sticky top-0 z-10">
|
|
|
|
|
<div className="w-16 flex-shrink-0 p-2 font-semibold text-sm border-r">
|
|
|
|
|
Round
|
|
|
|
|
</div>
|
2025-10-16 01:36:14 -07:00
|
|
|
{teams.map((team) => {
|
|
|
|
|
const timer = timerMap.get(team.id);
|
|
|
|
|
// Determine if this team is on the clock
|
|
|
|
|
const currentPick = Math.ceil(currentPickNumber / teams.length);
|
|
|
|
|
const pickInRound = ((currentPickNumber - 1) % teams.length) + 1;
|
|
|
|
|
const isOddRound = currentPick % 2 === 1;
|
|
|
|
|
const currentTeamIndex = isOddRound ? pickInRound - 1 : teams.length - pickInRound;
|
|
|
|
|
const isOnClock = teams[currentTeamIndex]?.id === team.id;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={team.id}
|
|
|
|
|
className={`flex-1 min-w-[140px] p-2 text-center font-semibold text-sm border-r last:border-r-0 ${
|
|
|
|
|
team.id === userTeamId ? "bg-primary/10" : ""
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<div className="truncate" title={team.name}>
|
|
|
|
|
{team.name}
|
|
|
|
|
</div>
|
|
|
|
|
{timer && (
|
|
|
|
|
<div className="mt-1">
|
|
|
|
|
<DraftTimer timeRemaining={timer.timeRemaining} isActive={isOnClock} />
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-10-16 01:12:50 -07:00
|
|
|
</div>
|
2025-10-16 01:36:14 -07:00
|
|
|
);
|
|
|
|
|
})}
|
2025-10-16 01:12:50 -07:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Draft Rounds */}
|
|
|
|
|
{Array.from({ length: draftRounds }, (_, roundIndex) => {
|
|
|
|
|
const round = roundIndex + 1;
|
|
|
|
|
const isOddRound = round % 2 === 1;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div key={round} className="flex border-b border-border hover:bg-muted/30">
|
|
|
|
|
{/* Round Number */}
|
|
|
|
|
<div className="w-16 flex-shrink-0 p-2 text-center font-medium text-sm border-r bg-muted/30">
|
|
|
|
|
{round}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Picks for this round */}
|
|
|
|
|
{teams.map((team, teamIndex) => {
|
|
|
|
|
const pickNumber = getPickNumber(round, teamIndex);
|
|
|
|
|
const pick = pickMap.get(pickNumber);
|
|
|
|
|
const participant = pick ? participantMap.get(pick.participantId) : null;
|
|
|
|
|
const isCurrentPick = pickNumber === currentPickNumber;
|
|
|
|
|
const isPastPick = pickNumber < currentPickNumber;
|
|
|
|
|
const isUserTeam = team.id === userTeamId;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={team.id}
|
|
|
|
|
className={`flex-1 min-w-[140px] p-2 border-r last:border-r-0 ${
|
|
|
|
|
isUserTeam ? "bg-primary/5" : ""
|
|
|
|
|
} ${isCurrentPick ? "ring-2 ring-primary ring-inset" : ""}`}
|
|
|
|
|
>
|
|
|
|
|
<div className="flex flex-col gap-1">
|
|
|
|
|
{/* Pick Number */}
|
|
|
|
|
<div className="text-xs text-muted-foreground">
|
|
|
|
|
{round}.{String(isOddRound ? teamIndex + 1 : teams.length - teamIndex).padStart(2, "0")}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Participant or Status */}
|
|
|
|
|
{participant ? (
|
|
|
|
|
<div className="space-y-0.5">
|
|
|
|
|
<div className="font-medium text-sm truncate" title={participant.name}>
|
|
|
|
|
{participant.name}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-xs text-muted-foreground truncate">
|
|
|
|
|
{participant.sport}
|
|
|
|
|
</div>
|
|
|
|
|
{pick && pick.pickedByType !== "owner" && (
|
|
|
|
|
<div className="text-xs text-muted-foreground italic">
|
|
|
|
|
({pick.pickedByType})
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
) : isCurrentPick ? (
|
|
|
|
|
<div className="text-sm font-medium text-primary">
|
|
|
|
|
On the clock
|
|
|
|
|
</div>
|
|
|
|
|
) : isPastPick ? (
|
|
|
|
|
<div className="text-xs text-muted-foreground italic">
|
|
|
|
|
Skipped
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="text-xs text-muted-foreground">
|
|
|
|
|
-
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|