diff --git a/app/components/draft/DraftGrid.tsx b/app/components/draft/DraftGrid.tsx
new file mode 100644
index 0000000..384e226
--- /dev/null
+++ b/app/components/draft/DraftGrid.tsx
@@ -0,0 +1,155 @@
+import { useMemo } from "react";
+
+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;
+}
+
+interface DraftGridProps {
+ teams: Team[];
+ draftPicks: DraftPick[];
+ participants: Participant[];
+ draftRounds: number;
+ currentPickNumber: number;
+ userTeamId?: string;
+}
+
+export function DraftGrid({
+ teams,
+ draftPicks,
+ participants,
+ draftRounds,
+ currentPickNumber,
+ userTeamId,
+}: 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]);
+
+ // 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 (
+
+
+ {/* Header Row - Team Names */}
+
+
+ Round
+
+ {teams.map((team) => (
+
+ ))}
+
+
+ {/* Draft Rounds */}
+ {Array.from({ length: draftRounds }, (_, roundIndex) => {
+ const round = roundIndex + 1;
+ const isOddRound = round % 2 === 1;
+
+ return (
+
+ {/* Round Number */}
+
+ {round}
+
+
+ {/* 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 (
+
+
+ {/* Pick Number */}
+
+ {round}.{String(isOddRound ? teamIndex + 1 : teams.length - teamIndex).padStart(2, "0")}
+
+
+ {/* Participant or Status */}
+ {participant ? (
+
+
+ {participant.name}
+
+
+ {participant.sport}
+
+ {pick && pick.pickedByType !== "owner" && (
+
+ ({pick.pickedByType})
+
+ )}
+
+ ) : isCurrentPick ? (
+
+ On the clock
+
+ ) : isPastPick ? (
+
+ Skipped
+
+ ) : (
+
+ -
+
+ )}
+
+
+ );
+ })}
+
+ );
+ })}
+
+
+ );
+}
diff --git a/app/routes/leagues/$leagueId.draft.tsx b/app/routes/leagues/$leagueId.draft.tsx
index 1d19219..a79c937 100644
--- a/app/routes/leagues/$leagueId.draft.tsx
+++ b/app/routes/leagues/$leagueId.draft.tsx
@@ -9,7 +9,9 @@ import {
findDraftSlotsBySeasonId,
getDraftPicks,
getSeasonTimers,
+ findSeasonWithSportsSeasons,
} from "~/models";
+import { DraftGrid } from "~/components/draft/DraftGrid";
export async function loader(args: LoaderFunctionArgs) {
const { userId } = await getAuth(args);
@@ -33,6 +35,12 @@ export async function loader(args: LoaderFunctionArgs) {
throw new Response("No active season found", { status: 404 });
}
+ // Get season with sports and participants
+ const seasonWithSports = await findSeasonWithSportsSeasons(season.id);
+ const participants = seasonWithSports?.seasonSports?.flatMap(
+ (ss: any) => ss.sportsSeason?.participants || []
+ ) || [];
+
// Get teams and draft order
const teams = await findTeamsBySeasonId(season.id);
const draftSlots = await findDraftSlotsBySeasonId(season.id);
@@ -67,6 +75,7 @@ export async function loader(args: LoaderFunctionArgs) {
isCommissioner: isUserCommissioner,
userTeam,
draftPicks,
+ participants,
timers,
currentPickNumber,
totalPicks,
@@ -79,6 +88,9 @@ export default function DraftRoom({ loaderData }: { loaderData: Awaited
{/* Header */}
-
+
{league.name} Draft
@@ -115,7 +127,7 @@ export default function DraftRoom({ loaderData }: { loaderData: Awaited
{/* Main Content */}
-
+
{isDraftComplete ? (
Draft Complete!
@@ -124,29 +136,31 @@ export default function DraftRoom({ loaderData }: { loaderData: Awaited
) : (
-
- {/* Left Column - Draft Grid & Player List */}
-
- {/* Draft Grid Placeholder */}
-
-
Draft Board
-
- Draft grid will be implemented in Phase 5
-
-
+
+ {/* Draft Board - Full Width */}
+
+
Draft Board
+
+
- {/* Player List Placeholder */}
+ {/* 2x2 Grid Below */}
+
+ {/* Available Players */}
Available Players
Player list will be implemented in Phase 6
-
- {/* Right Column - Queue & Controls */}
-
- {/* Queue Placeholder */}
+ {/* Your Queue */}
Your Queue
@@ -154,7 +168,7 @@ export default function DraftRoom({ loaderData }: { loaderData: Awaited
- {/* Timer Placeholder */}
+ {/* Draft Timer */}
Draft Timer
@@ -163,13 +177,27 @@ export default function DraftRoom({ loaderData }: { loaderData: Awaited
{/* Commissioner Controls */}
- {isCommissioner && (
+ {isCommissioner ? (
Commissioner Controls
Controls will be implemented in Phase 9
+ ) : (
+
+
Draft Info
+
+
+ Current Pick:
+ {currentPickNumber} of {totalPicks}
+
+
+ Draft Rounds:
+ {season.draftRounds}
+
+
+
)}