feat: add draft timer display with team-specific countdown UI

This commit is contained in:
Chris Parsons 2025-10-16 01:36:14 -07:00
parent 8a61085f3b
commit c820dfbdad
3 changed files with 91 additions and 12 deletions

View file

@ -1,4 +1,5 @@
import { useMemo } from "react";
import { DraftTimer } from "./DraftTimer";
interface Team {
id: string;
@ -23,6 +24,11 @@ interface Participant {
sport: string;
}
interface Timer {
teamId: string;
timeRemaining: number;
}
interface DraftGridProps {
teams: Team[];
draftPicks: DraftPick[];
@ -30,6 +36,7 @@ interface DraftGridProps {
draftRounds: number;
currentPickNumber: number;
userTeamId?: string;
timers?: Timer[];
}
export function DraftGrid({
@ -39,6 +46,7 @@ export function DraftGrid({
draftRounds,
currentPickNumber,
userTeamId,
timers = [],
}: DraftGridProps) {
// Create a map of participant IDs to participant data for quick lookup
const participantMap = useMemo(() => {
@ -50,6 +58,11 @@ export function DraftGrid({
return new Map(draftPicks.map((p) => [p.pickNumber, p]));
}, [draftPicks]);
// Create a map of team IDs to timers
const timerMap = useMemo(() => {
return new Map(timers.map((t) => [t.teamId, t]));
}, [timers]);
// Calculate pick number for a given round and team
const getPickNumber = (round: number, teamIndex: number): number => {
const isOddRound = round % 2 === 1;
@ -66,18 +79,33 @@ export function DraftGrid({
<div className="w-16 flex-shrink-0 p-2 font-semibold text-sm border-r">
Round
</div>
{teams.map((team) => (
<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}
{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>
)}
</div>
</div>
))}
);
})}
</div>
{/* Draft Rounds */}

View file

@ -0,0 +1,37 @@
interface DraftTimerProps {
timeRemaining: number; // in seconds
isActive?: boolean;
}
export function DraftTimer({ timeRemaining, isActive = false }: DraftTimerProps) {
// Format time as M:SS or H:MM:SS
const formatTime = (seconds: number): string => {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
if (hours > 0) {
return `${hours}:${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
}
return `${minutes}:${String(secs).padStart(2, "0")}`;
};
// Determine color based on time remaining
const getColorClass = (): string => {
if (timeRemaining > 60) {
return "text-green-600 dark:text-green-400";
} else if (timeRemaining > 30) {
return "text-yellow-600 dark:text-yellow-400";
} else if (timeRemaining > 10) {
return "text-red-600 dark:text-red-400";
} else {
return "text-red-600 dark:text-red-400 animate-pulse";
}
};
return (
<div className={`text-sm font-mono font-semibold ${getColorClass()} ${isActive ? "font-bold" : ""}`}>
{formatTime(timeRemaining)}
</div>
);
}

View file

@ -69,7 +69,19 @@ export async function loader(args: LoaderFunctionArgs) {
// Get draft data
const draftPicks = await getDraftPicks(season.id);
const timers = await getSeasonTimers(season.id);
let timers = await getSeasonTimers(season.id);
// If no timers exist, create default timers for display
if (timers.length === 0) {
timers = teams.map((team) => ({
id: team.id,
seasonId: season.id,
teamId: team.id,
timeRemaining: season.draftInitialTime,
createdAt: new Date(),
updatedAt: new Date(),
}));
}
// Get user's queue if they have a team
const queueItems = userTeam ? await getTeamQueue(userTeam.id) : [];
@ -105,6 +117,7 @@ export default function DraftRoom({ loaderData }: { loaderData: Awaited<ReturnTy
draftPicks,
participants,
queueItems,
timers,
isCommissioner,
userTeam,
currentPickNumber,
@ -161,6 +174,7 @@ export default function DraftRoom({ loaderData }: { loaderData: Awaited<ReturnTy
draftRounds={season.draftRounds}
currentPickNumber={currentPickNumber}
userTeamId={userTeam?.id}
timers={timers}
/>
</div>