import { useEffect, useState } from "react"; import { Link, useSearchParams } from "react-router"; import { getAuth } from "@clerk/react-router/server"; import { toast } from "sonner"; import { format } from "date-fns"; import { findLeagueById, findCurrentSeason, findTeamsBySeasonId, findCommissionersByLeagueId, isUserLeagueMember, isCommissioner, findUserByClerkId, findDraftSlotsBySeasonId, } from "~/models"; import { findSeasonWithSportsSeasons } from "~/models/season"; import type { Route } from "./+types/$leagueId"; import { Button } from "~/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card"; export async function loader(args: Route.LoaderArgs) { const { userId } = await getAuth(args); const { params } = args; const { leagueId } = params; // Fetch league const league = await findLeagueById(leagueId); if (!league) { throw new Response("League not found", { status: 404 }); } // Fetch current season with sports const season = await findCurrentSeason(leagueId); const seasonWithSports = season ? await findSeasonWithSportsSeasons(season.id) : null; // Fetch commissioners const commissioners = await findCommissionersByLeagueId(leagueId); // Check if current user is a commissioner const isUserCommissioner = userId ? await isCommissioner(leagueId, userId) : false; // Check if user is a member (has a team in current season) const isUserMember = userId ? await isUserLeagueMember(leagueId, userId) : false; // Check access: user must be a commissioner, a member, or an admin // If not logged in or not authorized, throw 403 if (!userId) { throw new Response("You must be logged in to view this league", { status: 401, }); } if (!isUserCommissioner && !isUserMember) { throw new Response("You do not have access to this league", { status: 403, }); } // Fetch teams for current season const teams = season ? await findTeamsBySeasonId(season.id) : []; // Fetch draft slots if season is in pre_draft or draft status const draftSlots = season && (season.status === "pre_draft" || season.status === "draft") ? await findDraftSlotsBySeasonId(season.id) : []; // Fetch user data for team owners const ownerIds = teams .map((t) => t.ownerId) .filter((id): id is string => id !== null); const uniqueOwnerIds = [...new Set(ownerIds)]; const owners = await Promise.all( uniqueOwnerIds.map(async (ownerId) => { const user = await findUserByClerkId(ownerId); return user ? { clerkId: ownerId, name: user.username || user.displayName } : null; }) ); const ownerMap = new Map( owners .filter((o): o is NonNullable => o !== null) .map((o) => [o.clerkId, o.name]) ); // Fetch user data for commissioners const commissionerIds = commissioners.map((c) => c.userId); const commissionerUsers = await Promise.all( commissionerIds.map(async (commissionerId) => { const user = await findUserByClerkId(commissionerId); return user ? { clerkId: commissionerId, name: user.username || user.displayName } : null; }) ); const commissionerMap = new Map( commissionerUsers .filter((c): c is NonNullable => c !== null) .map((c) => [c.clerkId, c.name]) ); // Count available teams const availableTeamCount = teams.filter((t) => !t.ownerId).length; // Count teams with owners const teamsWithOwners = teams.filter((t) => t.ownerId !== null).length; // Count sports in season const sportsCount = seasonWithSports?.seasonSports?.length || 0; // Check if draft order is set const isDraftOrderSet = draftSlots.length > 0; return { league, season, teams, commissioners, currentUserId: userId, isUserCommissioner, ownerMap: Object.fromEntries(ownerMap), commissionerMap: Object.fromEntries(commissionerMap), availableTeamCount, draftSlots, sportsCount, teamsWithOwners, isDraftOrderSet, }; } export default function LeagueHome({ loaderData }: Route.ComponentProps) { const { league, season, teams, commissioners, currentUserId, isUserCommissioner, ownerMap, commissionerMap, availableTeamCount, draftSlots, sportsCount, teamsWithOwners, isDraftOrderSet, } = loaderData; const [searchParams, setSearchParams] = useSearchParams(); const [copied, setCopied] = useState(false); useEffect(() => { if (searchParams.get("updated") === "true") { toast.success("Team updated successfully"); setSearchParams({}); } if (searchParams.get("joined") === "true") { toast.success("Welcome to the league! You've been assigned a team."); setSearchParams({}); } if (searchParams.get("left") === "true") { toast.success("You have left the league. Your team is now available."); setSearchParams({}); } }, [searchParams, setSearchParams]); const handleCopyInviteLink = async () => { if (!season?.inviteCode) return; const inviteUrl = `${window.location.origin}/i/${season.inviteCode}`; try { await navigator.clipboard.writeText(inviteUrl); setCopied(true); toast.success("Invite link copied to clipboard!"); setTimeout(() => setCopied(false), 2000); } catch { toast.error("Failed to copy invite link"); } }; return (

{league.name}

{teams.find((t) => t.ownerId === currentUserId) && ( )} {isUserCommissioner && ( )}
{season && (

{season.year} Season • Status:{" "} {season.status.replace("_", " ")}

)}
{/* Left Column - 2/3 width on desktop */}
{isUserCommissioner && season && availableTeamCount > 0 && ( Invite Link Share this link to invite people to join your league ( {availableTeamCount} spot{availableTeamCount !== 1 ? "s" : ""}{" "} available)
e.currentTarget.select()} />

Anyone with this link can join your league

)} Teams {teams.length} team{teams.length !== 1 ? "s" : ""} in this league
{teams.map((team) => { const isOwned = team.ownerId === currentUserId; const ownerName = team.ownerId ? ownerMap[team.ownerId] : null; return ( {team.name} {team.ownerId ? ( isOwned ? ( Your Team ) : ( {ownerName || "Unknown Owner"} ) ) : ( Available )} ); })}
{/* Right Column - 1/3 width on desktop */}
{/* Draft Order Display - Only show during pre_draft and draft */} {season && (season.status === "pre_draft" || season.status === "draft") && draftSlots.length > 0 && (
Draft Order {season.status === "pre_draft" ? "Upcoming draft order" : "Current draft order"}
{draftSlots.map((slot: any, index: number) => { const ownerName = slot.team.ownerId ? ownerMap[slot.team.ownerId] : null; return (
{index + 1}

{slot.team.name}

{ownerName && (

{ownerName}

)}
); })}
)} League Info

Created

{new Date(league.createdAt).toLocaleDateString()}

{season && ( <>

Season Status

{season.status.replace("_", " ")}

Teams Filled

{teamsWithOwners} of {teams.length}

Draft Order Set

{isDraftOrderSet ? "Yes" : "No"}

{!isDraftOrderSet && isUserCommissioner && ( )}

Draft Rounds

{season.draftRounds}

Sports Selected

{sportsCount}

Draft Board

View Draft Board

Standings

View Standings

Flex Spots

{Math.max(0, season.draftRounds - sportsCount)}

{season.draftDateTime && (

Draft Date

{format(new Date(season.draftDateTime), "PPP 'at' p")}

)} )}
Commissioners {commissioners.length} commissioner {commissioners.length !== 1 ? "s" : ""}
{commissioners.map((commissioner) => { const commissionerName = commissionerMap[commissioner.userId]; return (

{commissionerName || "Unknown User"} {commissioner.userId === currentUserId && ( (You) )}

); })}
); }