import { Link } from "react-router";
import { useState } from "react";
import type { Route } from "./+types/admin._index";
import { findAllSports } from "~/models/sport";
import { findAllSportsSeasons } from "~/models/sports-season";
import { findAllSeasonTemplates } from "~/models/season-template";
import { getEventsForDates } from "~/models/scoring-event";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import { Button } from "~/components/ui/button";
import { Badge } from "~/components/ui/badge";
import { Trophy, Calendar, FolderKanban, ArrowRight, CheckCircle2, Clock, ExternalLink } from "lucide-react";
export function meta(): Route.MetaDescriptors {
return [{ title: "Admin - Brackt" }];
}
function toDateStr(d: Date) {
return d.toISOString().split("T")[0];
}
function getTodayAndTomorrowDates() {
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(today.getDate() + 1);
return { today: toDateStr(today), tomorrow: toDateStr(tomorrow) };
}
export async function loader() {
const { today, tomorrow } = getTodayAndTomorrowDates();
const [sports, sportsSeasons, templates, upcomingEvents] = await Promise.all([
findAllSports(),
findAllSportsSeasons(),
findAllSeasonTemplates(),
getEventsForDates([today, tomorrow]),
]);
return {
stats: {
sportsCount: sports.length,
sportsSeasonsCount: sportsSeasons.length,
templatesCount: templates.length,
},
upcomingEvents,
today,
tomorrow,
};
}
function formatEventTime(eventStartsAt: string | null): string | null {
if (!eventStartsAt) return null;
const d = new Date(eventStartsAt);
return d.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
}
function getEventTypeLabel(eventType: string): string {
switch (eventType) {
case "playoff_game": return "Bracket";
case "major_tournament": return "Tournament";
case "final_standings": return "Final Standings";
default: return eventType;
}
}
type DashboardEvent = {
id: string;
name: string;
eventDate: string | null;
eventStartsAt: string | null;
eventType: string;
isComplete: boolean;
completedAt: string | null;
sportsSeasonId: string;
sportsSeasonName: string;
sportName: string;
sportSlug: string | null;
};
function GamesToScore({
events,
today,
tomorrow,
}: {
events: DashboardEvent[];
today: string;
tomorrow: string;
}) {
const [activeTab, setActiveTab] = useState<"today" | "tomorrow">("today");
const todayEvents = events.filter((e) => e.eventDate === today);
const tomorrowEvents = events.filter((e) => e.eventDate === tomorrow);
const displayEvents = activeTab === "today" ? todayEvents : tomorrowEvents;
const scored = displayEvents.filter((e) => e.isComplete).length;
const total = displayEvents.length;
const pending = total - scored;
return (
scored
Manage sports, seasons, participants, and templates
Active sports in the system
Across all sports
Season templates available
Create Sports
Add sports like NFL, NBA, Golf, etc.
Create Sports Seasons
Add specific seasons for each sport
Add Participants
Add teams or players to each sports season
Create Templates
Bundle sports seasons into templates for leagues