import { useLoaderData } from "react-router"; import { eq, asc, and } from "drizzle-orm"; import { getAuth } from "@clerk/react-router/server"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { DraftGrid } from "~/components/DraftGrid"; import { useDraftSocket } from "~/hooks/useDraftSocket"; import { useState, useEffect } from "react"; export async function loader(args: any) { const { params } = args; const { seasonId } = params; const auth = await getAuth(args); const userId = (auth as any).userId as string | null; if (!seasonId) { throw new Response("Season ID is required", { status: 400 }); } const db = database(); // Get season details const season = await db.query.seasons.findFirst({ where: eq(schema.seasons.id, seasonId), with: { league: true, }, }); if (!season) { throw new Response("Season not found", { status: 404 }); } // Check access: allow if public OR user is a league member/commissioner let hasAccess = season.league.isPublicDraftBoard; if (!hasAccess && userId) { // Check if user is a commissioner const isCommissioner = await db.query.commissioners.findFirst({ where: and( eq(schema.commissioners.leagueId, season.leagueId), eq(schema.commissioners.userId, userId) ), }); // Check if user has a team in this season const hasTeam = await db.query.teams.findFirst({ where: and( eq(schema.teams.seasonId, seasonId), eq(schema.teams.ownerId, userId) ), }); hasAccess = !!(isCommissioner || hasTeam); } if (!hasAccess) { throw new Response("This draft board is not public", { status: 403 }); } // Get draft slots (draft order) const draftSlots = await db .select({ id: schema.draftSlots.id, draftOrder: schema.draftSlots.draftOrder, team: schema.teams, }) .from(schema.draftSlots) .innerJoin(schema.teams, eq(schema.draftSlots.teamId, schema.teams.id)) .where(eq(schema.draftSlots.seasonId, seasonId)) .orderBy(asc(schema.draftSlots.draftOrder)); // Get all draft picks with participant and sport info const draftPicks = await db .select({ id: schema.draftPicks.id, pickNumber: schema.draftPicks.pickNumber, round: schema.draftPicks.round, pickInRound: schema.draftPicks.pickInRound, team: schema.teams, participant: schema.participants, sport: schema.sports, }) .from(schema.draftPicks) .innerJoin(schema.teams, eq(schema.draftPicks.teamId, schema.teams.id)) .innerJoin( schema.participants, eq(schema.draftPicks.participantId, schema.participants.id) ) .innerJoin( schema.sportsSeasons, eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id) ) .innerJoin(schema.sports, eq(schema.sportsSeasons.sportId, schema.sports.id)) .where(eq(schema.draftPicks.seasonId, seasonId)) .orderBy(asc(schema.draftPicks.pickNumber)); return { season, draftSlots, draftPicks, }; } export default function DraftBoard() { const { season, draftSlots, draftPicks: initialPicks } = useLoaderData(); const { isConnected, on, off } = useDraftSocket(season.id); const [picks, setPicks] = useState(initialPicks); const [currentPick, setCurrentPick] = useState(season.currentPickNumber || 1); // Listen for new picks (only if draft is still active) useEffect(() => { if (season.status !== "draft") return; const handlePickMade = (data: any) => { setPicks((prev: any) => [...prev, data.pick]); setCurrentPick(data.nextPickNumber); }; on("pick-made", handlePickMade); return () => { off("pick-made", handlePickMade); }; }, [on, off, season.status]); // Generate draft grid const totalTeams = draftSlots.length; const totalRounds = season.draftRounds || 1; const draftGrid: any[][] = []; for (let round = 0; round < totalRounds; round++) { const roundPicks: any[] = []; for (let teamIndex = 0; teamIndex < totalTeams; teamIndex++) { const pickNumber = round * totalTeams + teamIndex + 1; const pick = picks.find((p: any) => p.pickNumber === pickNumber); roundPicks.push(pick || null); } draftGrid.push(roundPicks); } return (
{/* Header */}

{season.league.name} - {season.year} Draft Board

Round: {totalTeams > 0 ? Math.ceil(currentPick / totalTeams) : 1} Pick: {currentPick} {season.status.replace("_", " ")}
{season.status === "draft" && (
{isConnected ? "Live" : "Disconnected"}
)}
{/* Draft Grid */}
); }