brackt/app/routes/admin.sports-seasons.$id.events.server.ts
Chris Parsons 1089022c09 feat: Implement Qualifying Points Standings component and related logic
- Added `QualifyingPointsStandings` component to display standings based on qualifying points.
- Introduced scoring rules and projected points calculation for participants.
- Implemented tie handling for rankings and displayed appropriate UI elements based on finalization status.
- Created tests for qualifying points configuration, accumulation logic, ranking logic, and scoring workflow.
- Developed scoring calculator tests to validate fantasy points conversion from qualifying points.
- Established qualifying points management functions including initialization, retrieval, updating, and resetting of points.
2025-11-11 10:08:25 -08:00

118 lines
3.9 KiB
TypeScript

import type { Route } from "./+types/admin.sports-seasons.$id.events";
import { redirect } from "react-router";
import { findSportsSeasonById } from "~/models/sports-season";
import {
getScoringEventsForSportsSeason,
createScoringEvent,
deleteScoringEvent,
type CreateScoringEventData,
} from "~/models/scoring-event";
import { getQPStandings } from "~/models/qualifying-points";
import { finalizeQualifyingPoints } from "~/models/scoring-calculator";
import { getScoringRules } from "~/models/scoring-rules";
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;
let 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) {
console.error("Error finalizing qualifying points:", error);
return { error: error instanceof Error ? error.message : "Failed to finalize qualifying points" };
}
}
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) {
console.error("Error deleting event:", error);
return { error: "Failed to delete event" };
}
}
const name = formData.get("name");
const eventType = formData.get("eventType");
const eventDate = formData.get("eventDate");
// Validation
if (typeof name !== "string" || !name.trim()) {
return { error: "Event name is required" };
}
if (
eventType !== "playoff_game" &&
eventType !== "major_tournament" &&
eventType !== "final_standings"
) {
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";
const eventData: CreateScoringEventData = {
sportsSeasonId: params.id,
name: name.trim(),
eventType: eventType as "playoff_game" | "major_tournament" | "final_standings",
eventDate: typeof eventDate === "string" && eventDate ? new Date(eventDate) : undefined,
isQualifyingEvent,
};
try {
const event = await createScoringEvent(eventData);
return redirect(`/admin/sports-seasons/${params.id}/events/${event.id}`);
} catch (error) {
console.error("Error creating scoring event:", error);
return { error: "Failed to create scoring event. Please try again." };
}
}