import { redirect, useFetcher, useLoaderData, useRevalidator, Link } from "react-router"; import { useCallback, useEffect, useMemo, useState } from "react"; import { ArrowLeft, ListOrdered } from "lucide-react"; import { auth } from "~/lib/auth.server"; import { findSeasonWithTeamsAndLeague } from "~/models/season"; import { getDraftParticipants } from "~/models/season-participant"; import { getTeamQueue } from "~/models/draft-queue"; import { QueueSection } from "~/components/draft/QueueSection"; import { Button } from "~/components/ui/button"; import { Badge } from "~/components/ui/badge"; import type { Route } from "./+types/$leagueId.draft-queue.$seasonId"; export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { return [{ title: `Pre-Draft Queue — ${data?.season?.league?.name ?? "League"} - Brackt` }]; } export async function loader(args: Route.LoaderArgs) { const { params } = args; const { seasonId, leagueId } = params; const session = await auth.api.getSession({ headers: args.request.headers }); const userId = session?.user.id ?? null; if (!seasonId) { throw new Response("Season ID is required", { status: 400 }); } const season = await findSeasonWithTeamsAndLeague(seasonId); if (!season) { throw new Response("Season not found", { status: 404 }); } // Redirect if draft has started — send them to the appropriate page if (season.status === "draft") { return redirect(`/leagues/${leagueId}/draft/${seasonId}`); } if (season.status === "active" || season.status === "completed") { return redirect(`/leagues/${leagueId}/draft-board/${seasonId}`); } if (!userId) { throw new Response("You must be logged in to build a queue", { status: 401 }); } const userTeam = season.teams.find((t) => t.ownerId === userId); if (!userTeam) { throw new Response("You do not have a team in this season", { status: 403 }); } const [availableParticipants, userQueue] = await Promise.all([ getDraftParticipants(seasonId), getTeamQueue(userTeam.id), ]); return { season, leagueId, userTeam, availableParticipants, userQueue, }; } type QueueItem = { id: string; participantId: string }; export default function PreDraftQueue() { const { season, leagueId, userTeam, availableParticipants, userQueue } = useLoaderData(); const { revalidate } = useRevalidator(); const [localQueue, setLocalQueue] = useState(userQueue); // Sync local state when server data refreshes useEffect(() => { setLocalQueue(userQueue); }, [userQueue]); const addFetcher = useFetcher<{ success: boolean }>(); const removeFetcher = useFetcher(); const reorderFetcher = useFetcher(); // After add completes, revalidate to replace the optimistic temp ID with the real one useEffect(() => { if (addFetcher.state === "idle" && addFetcher.data?.success) { revalidate(); } }, [addFetcher.state, addFetcher.data, revalidate]); const queuedParticipantIds = useMemo( () => new Set(localQueue.map((q) => q.participantId)), [localQueue] ); const handleAdd = useCallback( (participantId: string) => { if (queuedParticipantIds.has(participantId)) return; // Optimistic update with a temporary ID const tempId = `temp-${Date.now()}-${participantId}`; setLocalQueue((prev) => [...prev, { id: tempId, participantId }]); const formData = new FormData(); formData.append("seasonId", season.id); formData.append("teamId", userTeam.id); formData.append("participantId", participantId); addFetcher.submit(formData, { method: "POST", action: "/api/queue/add" }); }, [queuedParticipantIds, season.id, userTeam.id, addFetcher] ); const handleRemove = useCallback( (queueId: string) => { setLocalQueue((prev) => prev.filter((item) => item.id !== queueId)); const formData = new FormData(); formData.append("queueId", queueId); formData.append("teamId", userTeam.id); removeFetcher.submit(formData, { method: "POST", action: "/api/queue/remove" }); }, [userTeam.id, removeFetcher] ); const handleReorder = useCallback( (participantIds: string[]) => { setLocalQueue((prev) => { const byParticipantId = new Map(prev.map((item) => [item.participantId, item])); return participantIds .map((pid) => byParticipantId.get(pid)) .filter((item): item is (typeof prev)[number] => item !== undefined); }); const formData = new FormData(); formData.append("teamId", userTeam.id); formData.append("seasonId", season.id); formData.append("participantIds", JSON.stringify(participantIds)); reorderFetcher.submit(formData, { method: "POST", action: "/api/queue/reorder" }); }, [userTeam.id, season.id, reorderFetcher] ); return (
{/* Header */}
Back to {season.league.name}

Pre-Draft Queue

Rank the participants you want. Your queue will carry into the live draft and determine your autopick order.

{/* Two-panel layout */}
{/* Participant list */}

All Participants

Sorted by VORP
{availableParticipants.map((p) => { const inQueue = queuedParticipantIds.has(p.id); return (

{p.name}

{p.sport.name}

{p.vorpValue != null && ( {Number(p.vorpValue).toFixed(1)} )}
); })} {availableParticipants.length === 0 && (

No participants have been added to this season yet.

)}
{/* Queue panel */}

Your Queue

{localQueue.length}
{localQueue.length > 0 && (

Drag to reorder · Autopick follows this order

)}
); }