import { Form, Link, redirect } from "react-router"; import type { Route } from "./+types/admin.sports-seasons.new"; import { logger } from "~/lib/logger"; import { createSportsSeason, type NewSportsSeason } from "~/models/sports-season"; import { findAllSports } from "~/models/sport"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card"; import { useState } from "react"; const SELECT_CLASS = "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"; export function meta(): Route.MetaDescriptors { return [{ title: "New Sports Season - Brackt Admin" }]; } export async function loader() { const sports = await findAllSports(); return { sports }; } export async function action({ request }: Route.ActionArgs) { const formData = await request.formData(); const sportId = formData.get("sportId"); const name = formData.get("name"); const year = formData.get("year"); const startDate = formData.get("startDate"); const endDate = formData.get("endDate"); const status = formData.get("status"); const scoringType = formData.get("scoringType"); const scoringPattern = formData.get("scoringPattern"); const totalMajors = formData.get("totalMajors"); const draftOn = formData.get("draftOn"); const draftOff = formData.get("draftOff"); const externalSeasonId = formData.get("externalSeasonId"); // Validation if (typeof sportId !== "string" || !sportId) { return { error: "Sport is required" }; } if (typeof name !== "string" || !name.trim()) { return { error: "Season name is required" }; } if (typeof year !== "string") { return { error: "Year is required" }; } const yearNum = parseInt(year, 10); if (isNaN(yearNum) || yearNum < 2000 || yearNum > 2100) { return { error: "Year must be between 2000 and 2100" }; } if (status !== "upcoming" && status !== "active" && status !== "completed") { return { error: "Invalid status" }; } if (scoringType !== "playoffs" && scoringType !== "regular_season" && scoringType !== "majors") { return { error: "Invalid scoring type" }; } const validScoringPatterns = ["playoff_bracket", "season_standings", "qualifying_points"]; if (scoringPattern && typeof scoringPattern === "string" && !validScoringPatterns.includes(scoringPattern)) { return { error: "Invalid scoring pattern" }; } if (typeof draftOn !== "string" || !draftOn) { return { error: "Draft open date is required" }; } if (typeof draftOff !== "string" || !draftOff) { return { error: "Draft close date is required" }; } if (draftOff < draftOn) { return { error: "Draft close date must be on or after draft open date" }; } try { const seasonData: Partial = { sportId, name: name.trim(), year: yearNum, startDate: typeof startDate === "string" && startDate ? startDate : null, endDate: typeof endDate === "string" && endDate ? endDate : null, status, scoringType, draftOn, draftOff, }; if (scoringPattern && typeof scoringPattern === "string") { seasonData.scoringPattern = scoringPattern as "playoff_bracket" | "season_standings" | "qualifying_points"; } if (totalMajors && typeof totalMajors === "string") { const totalMajorsNum = parseInt(totalMajors, 10); if (!isNaN(totalMajorsNum) && totalMajorsNum > 0) { seasonData.totalMajors = totalMajorsNum; } } if (typeof externalSeasonId === "string" && externalSeasonId.trim()) { seasonData.externalSeasonId = externalSeasonId.trim(); } await createSportsSeason(seasonData as NewSportsSeason); return redirect("/admin/sports-seasons"); } catch (error) { logger.error("Error creating sports season:", error); return { error: "Failed to create sports season. Please try again." }; } } export default function NewSportsSeason({ loaderData, actionData }: Route.ComponentProps) { const { sports } = loaderData; const currentYear = new Date().getFullYear(); const [scoringPattern, setScoringPattern] = useState(""); return (

Create New Sports Season

Add a new season for a sport

Sports Season Details Enter the information for the new sports season
{sports.length === 0 && (

No sports available. Create a sport first.

)}

Playoffs: Team sports playoffs. Regular Season: Full season standings. Majors: Individual sport majors.

Qualifying Points: For sports like Golf/Tennis where participants earn points across majors, then top 8 get fantasy points.

{scoringPattern === "qualifying_points" && (

How many major tournaments will be tracked? (e.g., Golf has 4 majors, Tennis has 4 Grand Slams)

)}

Used by the match sync cron job. For CS2: PandaScore serie_id. For MLB/NBA: year (e.g., 2025).

This season appears in league creation and pre-draft settings only between these two dates (inclusive).

{actionData?.error && (
{actionData.error}
)}
); }