import type { Route } from "./+types/admin.sports-seasons.$id.events"; import { redirect } from "react-router"; import { logger } from "~/lib/logger"; import { findSportsSeasonById } from "~/models/sports-season"; import { getScoringEventsForSportsSeason, createScoringEvent, deleteScoringEvent, bulkCreateScoringEvents, type CreateScoringEventData, } from "~/models/scoring-event"; import { getQPStandings } from "~/models/qualifying-points"; import { finalizeQualifyingPoints } from "~/models/scoring-calculator"; export async function loader({ params }: Route.LoaderArgs) { const sportsSeason = await findSportsSeasonById(params.id); if (!sportsSeason) { throw new Response("Sports season not found", { status: 404 }); } const events = await getScoringEventsForSportsSeason(params.id); // For qualifying sports seasons, get QP standings let qpStandings = null; const scoringRules = null; if (sportsSeason.scoringPattern === "qualifying_points") { qpStandings = await getQPStandings(params.id); // Get scoring rules from a linked season (if any) // For now, we'll use default scoring for projection // Note: QP standings are global to the sports season, not league-specific // When showing to league members, we would filter by their league's scoring rules } return { sportsSeason: sportsSeason as typeof sportsSeason & { sport: { id: string; name: string; type: string; slug: string }; }, events, qpStandings, scoringRules, }; } export async function action({ request, params }: Route.ActionArgs) { const formData = await request.formData(); const intent = formData.get("intent"); if (intent === "finalize-qp") { try { await finalizeQualifyingPoints(params.id); return { success: "Qualifying points finalized and fantasy placements assigned to top 8!" }; } catch (error) { logger.error("Error finalizing qualifying points:", error); return { error: error instanceof Error ? error.message : "Failed to finalize qualifying points" }; } } if (intent === "bulk-create") { const bulkText = formData.get("bulkEvents"); const bulkEventType = formData.get("bulkEventType"); if (typeof bulkText !== "string" || !bulkText.trim()) { return { error: "Event list is required" }; } const validTypes = ["playoff_game", "major_tournament", "schedule_event"] as const; type ValidEventType = typeof validTypes[number]; const eventType: ValidEventType = validTypes.includes(bulkEventType as ValidEventType) ? (bulkEventType as ValidEventType) : "playoff_game"; const sportsSeason = await findSportsSeasonById(params.id); if (!sportsSeason) return { error: "Sports season not found" }; const isQualifyingDefault = sportsSeason.scoringPattern === "qualifying_points" && eventType === "major_tournament"; // Parse lines: "Name, YYYY-MM-DD[, HH:MM]" or tab-separated, or just "Name" const lines = bulkText .split("\n") .map((l) => l.trim()) .filter(Boolean); const parsedEvents = lines.map((line) => { const parts = line.split(/[,\t]/).map((p) => p.trim()); const name = parts[0]; const dateStr = parts[1]; const timeStr = parts[2]; const eventDate = dateStr && /^\d{4}-\d{2}-\d{2}$/.test(dateStr) ? new Date(dateStr) : undefined; const eventStartsAt = eventDate && dateStr && timeStr && /^\d{2}:\d{2}$/.test(timeStr) ? new Date(`${dateStr}T${timeStr}:00.000Z`) : undefined; return { name, eventDate, eventStartsAt, eventType, isQualifyingEvent: isQualifyingDefault }; }); const validEvents = parsedEvents.filter((e) => e.name); if (validEvents.length === 0) { return { error: "No valid events found. Use format: Event Name, YYYY-MM-DD" }; } try { await bulkCreateScoringEvents(params.id, validEvents); return { success: `Created ${validEvents.length} event${validEvents.length !== 1 ? "s" : ""} successfully` }; } catch (error) { logger.error("Error bulk creating events:", error); return { error: "Failed to create events. Please try again." }; } } if (intent === "delete-event") { const eventId = formData.get("eventId"); if (typeof eventId !== "string" || !eventId) { return { error: "Event ID is required" }; } try { await deleteScoringEvent(eventId); return { success: "Event deleted successfully" }; } catch (error) { logger.error("Error deleting event:", error); return { error: "Failed to delete event" }; } } const name = formData.get("name"); const eventType = formData.get("eventType"); const eventStartsAtRaw = formData.get("eventStartsAt"); // Validation if (typeof name !== "string" || !name.trim()) { return { error: "Event name is required" }; } if ( eventType !== "playoff_game" && eventType !== "major_tournament" && eventType !== "schedule_event" ) { return { error: "Invalid event type" }; } // Get the sports season to check if this should be a qualifying event const sportsSeason = await findSportsSeasonById(params.id); if (!sportsSeason) { return { error: "Sports season not found" }; } // Automatically mark major tournaments as qualifying events for qualifying_points sports seasons const isQualifyingEvent = sportsSeason.scoringPattern === "qualifying_points" && eventType === "major_tournament"; // eventStartsAt is a UTC ISO string produced by localDateTimeToUtcIso on the client const eventStartsAt = typeof eventStartsAtRaw === "string" && eventStartsAtRaw ? new Date(eventStartsAtRaw) : undefined; // Derive eventDate from eventStartsAt (UTC date portion) const eventDate = eventStartsAt ? new Date(eventStartsAt.toISOString().split("T")[0]) : undefined; const eventData: CreateScoringEventData = { sportsSeasonId: params.id, name: name.trim(), eventType: eventType as "playoff_game" | "major_tournament" | "schedule_event", eventDate, eventStartsAt, isQualifyingEvent, }; try { const event = await createScoringEvent(eventData); return redirect(`/admin/sports-seasons/${params.id}/events/${event.id}`); } catch (error) { logger.error("Error creating scoring event:", error); return { error: "Failed to create scoring event. Please try again." }; } }