2025-10-11 01:03:31 -07:00
|
|
|
import { useEffect } from "react";
|
|
|
|
|
import { Link, useSearchParams } from "react-router";
|
2025-10-11 00:07:39 -07:00
|
|
|
import { getAuth } from "@clerk/react-router/server";
|
2025-10-11 01:03:31 -07:00
|
|
|
import { toast } from "sonner";
|
|
|
|
|
import {
|
|
|
|
|
findLeagueById,
|
|
|
|
|
findCurrentSeason,
|
|
|
|
|
findTeamsBySeasonId,
|
|
|
|
|
findCommissionersByLeagueId,
|
|
|
|
|
} from "~/models";
|
2025-10-11 00:07:39 -07:00
|
|
|
import type { Route } from "./+types/$leagueId";
|
2025-10-11 01:03:31 -07:00
|
|
|
import { Button } from "~/components/ui/button";
|
2025-10-11 00:07:39 -07:00
|
|
|
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
|
|
|
|
|
const season = await findCurrentSeason(leagueId);
|
|
|
|
|
|
|
|
|
|
// Fetch teams for current season
|
|
|
|
|
const teams = season ? await findTeamsBySeasonId(season.id) : [];
|
|
|
|
|
|
2025-10-11 00:29:04 -07:00
|
|
|
// Fetch commissioners
|
|
|
|
|
const commissioners = await findCommissionersByLeagueId(leagueId);
|
|
|
|
|
|
|
|
|
|
// Check if current user is a commissioner
|
|
|
|
|
const isUserCommissioner = commissioners.some(c => c.userId === userId);
|
|
|
|
|
|
2025-10-11 00:07:39 -07:00
|
|
|
return {
|
|
|
|
|
league,
|
|
|
|
|
season,
|
|
|
|
|
teams,
|
2025-10-11 00:29:04 -07:00
|
|
|
commissioners,
|
2025-10-11 00:07:39 -07:00
|
|
|
currentUserId: userId,
|
2025-10-11 00:29:04 -07:00
|
|
|
isUserCommissioner,
|
2025-10-11 00:07:39 -07:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function LeagueHome({ loaderData }: Route.ComponentProps) {
|
2025-10-11 00:29:04 -07:00
|
|
|
const { league, season, teams, commissioners, currentUserId, isUserCommissioner } = loaderData;
|
2025-10-11 01:03:31 -07:00
|
|
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (searchParams.get("updated") === "true") {
|
|
|
|
|
toast.success("League updated successfully");
|
|
|
|
|
// Remove the query parameter
|
|
|
|
|
setSearchParams({});
|
|
|
|
|
}
|
|
|
|
|
}, [searchParams, setSearchParams]);
|
2025-10-11 00:07:39 -07:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="container mx-auto py-8 px-4">
|
|
|
|
|
<div className="mb-8">
|
|
|
|
|
<div className="flex items-center justify-between mb-2">
|
|
|
|
|
<h1 className="text-4xl font-bold">{league.name}</h1>
|
2025-10-11 00:29:04 -07:00
|
|
|
<div className="flex gap-2">
|
|
|
|
|
{isUserCommissioner && (
|
|
|
|
|
<Button variant="outline" asChild>
|
|
|
|
|
<Link to={`/leagues/${league.id}/settings`}>Settings</Link>
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
<Button variant="outline" asChild>
|
|
|
|
|
<Link to="/">Back to Home</Link>
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
2025-10-11 00:07:39 -07:00
|
|
|
</div>
|
|
|
|
|
{season && (
|
|
|
|
|
<p className="text-muted-foreground">
|
|
|
|
|
{season.year} Season • Status:{" "}
|
|
|
|
|
<span className="capitalize">
|
|
|
|
|
{season.status.replace("_", " ")}
|
|
|
|
|
</span>
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
|
|
|
<Card className="md:col-span-2 lg:col-span-3">
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Teams</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
{teams.length} team{teams.length !== 1 ? "s" : ""} in this league
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
|
|
|
{teams.map((team) => {
|
|
|
|
|
const isOwned = team.ownerId === currentUserId;
|
|
|
|
|
return (
|
|
|
|
|
<Card
|
|
|
|
|
key={team.id}
|
|
|
|
|
className={isOwned ? "border-primary" : ""}
|
|
|
|
|
>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle className="text-lg">{team.name}</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
{team.ownerId ? (
|
|
|
|
|
isOwned ? (
|
|
|
|
|
<span className="text-primary font-medium">
|
|
|
|
|
Your Team
|
|
|
|
|
</span>
|
|
|
|
|
) : (
|
|
|
|
|
"Owned"
|
|
|
|
|
)
|
|
|
|
|
) : (
|
|
|
|
|
<span className="text-muted-foreground">
|
|
|
|
|
Available
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
</Card>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>League Info</CardTitle>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="space-y-2">
|
|
|
|
|
<div>
|
|
|
|
|
<p className="text-sm text-muted-foreground">Created</p>
|
|
|
|
|
<p className="font-medium">
|
|
|
|
|
{new Date(league.createdAt).toLocaleDateString()}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
{season && (
|
|
|
|
|
<div>
|
|
|
|
|
<p className="text-sm text-muted-foreground">Season Status</p>
|
|
|
|
|
<p className="font-medium capitalize">
|
|
|
|
|
{season.status.replace("_", " ")}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
2025-10-11 00:29:04 -07:00
|
|
|
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Commissioners</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
{commissioners.length} commissioner{commissioners.length !== 1 ? "s" : ""}
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{commissioners.map((commissioner, index) => (
|
|
|
|
|
<div
|
|
|
|
|
key={commissioner.id}
|
|
|
|
|
className="flex items-center justify-between py-2 border-b last:border-0"
|
|
|
|
|
>
|
|
|
|
|
<div>
|
|
|
|
|
<p className="font-medium">
|
|
|
|
|
Commissioner {index + 1}
|
|
|
|
|
{commissioner.userId === currentUserId && (
|
|
|
|
|
<span className="ml-2 text-xs text-primary">(You)</span>
|
|
|
|
|
)}
|
|
|
|
|
</p>
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
Since {new Date(commissioner.createdAt).toLocaleDateString()}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
2025-10-11 00:07:39 -07:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|