brackt/app/routes/admin.sports-seasons.tsx
Chris Parsons dc879e3033
feat: sort admin sports seasons by status, sport name, then year (#286)
Active seasons sort first, then upcoming, then completed. Within each
status group, seasons sort by sport name alphabetically then year
ascending. Fixes return type of findAllSportsSeasons to reflect the
included sport/participants relations, removing the cast in the loader.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 10:07:18 -04:00

151 lines
5.5 KiB
TypeScript

import { Link } from "react-router";
import type { Route } from "./+types/admin.sports-seasons";
import { findAllSportsSeasons } from "~/models/sports-season";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import { Button } from "~/components/ui/button";
import { Plus, Calendar } from "lucide-react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "~/components/ui/table";
import { Badge } from "~/components/ui/badge";
export function meta(): Route.MetaDescriptors {
return [{ title: "Sports Seasons - Brackt Admin" }];
}
export async function loader() {
const sportsSeasons = await findAllSportsSeasons();
return { sportsSeasons };
}
export default function AdminSportsSeasons({ loaderData }: Route.ComponentProps) {
const { sportsSeasons } = loaderData;
const today = new Date().toISOString().slice(0, 10);
return (
<div className="p-8">
<div className="flex items-center justify-between mb-6">
<div>
<h1 className="text-3xl font-bold">Sports Seasons</h1>
<p className="text-muted-foreground mt-1">
Manage sports seasons across all sports
</p>
</div>
<Button asChild>
<Link to="/admin/sports-seasons/new">
<Plus className="mr-2 h-4 w-4" />
Add Sports Season
</Link>
</Button>
</div>
<Card>
<CardHeader>
<CardTitle>All Sports Seasons</CardTitle>
<CardDescription>
{sportsSeasons.length} {sportsSeasons.length === 1 ? "season" : "seasons"} total
</CardDescription>
</CardHeader>
<CardContent>
{sportsSeasons.length === 0 ? (
<div className="text-center py-12">
<Calendar className="mx-auto h-12 w-12 text-muted-foreground" />
<h3 className="mt-4 text-lg font-semibold">No sports seasons yet</h3>
<p className="text-muted-foreground mt-2">
Get started by creating your first sports season
</p>
<Button className="mt-4" asChild>
<Link to="/admin/sports-seasons/new">
<Plus className="mr-2 h-4 w-4" />
Add Sports Season
</Link>
</Button>
</div>
) : (
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Sport</TableHead>
<TableHead>Year</TableHead>
<TableHead>Status</TableHead>
<TableHead>Draft Window</TableHead>
<TableHead>Scoring Type</TableHead>
<TableHead>Participants</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{sportsSeasons.map((season) => (
<TableRow key={season.id}>
<TableCell className="font-medium">{season.name}</TableCell>
<TableCell>{season.sport.name}</TableCell>
<TableCell>{season.year}</TableCell>
<TableCell>
<Badge
variant={
season.status === "active"
? "default"
: season.status === "completed"
? "secondary"
: "outline"
}
>
{season.status}
</Badge>
</TableCell>
<TableCell>
{(() => {
const active = season.draftOn <= today && today <= season.draftOff;
return (
<div className="space-y-0.5">
<Badge variant={active ? "default" : "secondary"}>
{active ? "Active" : "Inactive"}
</Badge>
<p className="text-xs text-muted-foreground whitespace-nowrap">
{season.draftOn} {season.draftOff}
</p>
</div>
);
})()}
</TableCell>
<TableCell className="text-muted-foreground capitalize">
{season.scoringType.replace("_", " ")}
</TableCell>
<TableCell className="text-muted-foreground">
{season.participants.length}
</TableCell>
<TableCell className="text-right">
<div className="flex items-center justify-end gap-2">
<Button variant="ghost" size="sm" asChild>
<Link to={`/admin/sports-seasons/${season.id}/participants`}>
Participants
</Link>
</Button>
<Button variant="ghost" size="sm" asChild>
<Link to={`/admin/sports-seasons/${season.id}`}>Edit</Link>
</Button>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)}
</CardContent>
</Card>
</div>
);
}