brackt/app/routes/admin.sports-seasons.$id.events.$eventId.server.ts

224 lines
6.9 KiB
TypeScript

import type { Route } from "./+types/admin.sports-seasons.$id.events.$eventId";
import { findSportsSeasonById } from "~/models/sports-season";
import { findParticipantsBySportsSeasonId } from "~/models/participant";
import {
getScoringEventById,
completeScoringEvent,
} from "~/models/scoring-event";
import {
getEventResults,
createEventResult,
updateEventResult,
deleteEventResult,
type CreateEventResultData,
type UpdateEventResultData,
} from "~/models/event-result";
import { findParticipantResultsBySportsSeasonId } from "~/models/participant-result";
import {
upsertParticipantSeasonResult,
getSeasonResults,
} from "~/models/participant-season-result";
import { processSeasonStandings } 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 event = await getScoringEventById(params.eventId);
if (!event) {
throw new Response("Event not found", { status: 404 });
}
const participants = await findParticipantsBySportsSeasonId(params.id);
const results = await getEventResults(params.eventId);
const participantResults = await findParticipantResultsBySportsSeasonId(params.id);
// For final_standings events, also get season results
let seasonResults = null;
if (event.eventType === "final_standings") {
seasonResults = await getSeasonResults(params.id);
}
return {
sportsSeason: sportsSeason as typeof sportsSeason & {
sport: { id: string; name: string; type: string; slug: string };
},
event,
participants,
results,
participantResults,
seasonResults,
};
}
export async function action({ request, params }: Route.ActionArgs) {
const formData = await request.formData();
const intent = formData.get("intent");
if (intent === "complete") {
try {
const event = await getScoringEventById(params.eventId);
if (!event) {
return { error: "Event not found" };
}
await completeScoringEvent(params.eventId);
// If this is a final_standings event, process season standings
if (event.eventType === "final_standings") {
await processSeasonStandings(params.id);
return { success: "Event completed and fantasy placements assigned to top 8!" };
}
return { success: "Event marked as completed" };
} catch (error) {
console.error("Error completing event:", error);
return { error: "Failed to complete event" };
}
}
if (intent === "add-result") {
const participantId = formData.get("participantId");
const placement = formData.get("placement");
if (typeof participantId !== "string" || !participantId) {
return { error: "Participant is required" };
}
if (typeof placement !== "string" || !placement) {
return { error: "Placement is required" };
}
const placementNum = parseInt(placement, 10);
if (isNaN(placementNum) || placementNum < 1 || placementNum > 100) {
return { error: "Placement must be between 1 and 100" };
}
const resultData: CreateEventResultData = {
scoringEventId: params.eventId,
participantId,
placement: placementNum,
};
try {
await createEventResult(resultData);
return { success: "Result added successfully" };
} catch (error) {
console.error("Error adding result:", error);
return { error: "Failed to add result" };
}
}
if (intent === "update-result") {
const resultId = formData.get("resultId");
const placement = formData.get("placement");
if (typeof resultId !== "string" || !resultId) {
return { error: "Result ID is required" };
}
if (typeof placement !== "string" || !placement) {
return { error: "Placement is required" };
}
const placementNum = parseInt(placement, 10);
if (isNaN(placementNum) || placementNum < 1 || placementNum > 100) {
return { error: "Placement must be between 1 and 100" };
}
const updateData: UpdateEventResultData = {
placement: placementNum,
};
try {
await updateEventResult(resultId, updateData);
return { success: "Result updated successfully" };
} catch (error) {
console.error("Error updating result:", error);
return { error: "Failed to update result" };
}
}
if (intent === "delete-result") {
const resultId = formData.get("resultId");
if (typeof resultId !== "string" || !resultId) {
return { error: "Result ID is required" };
}
try {
await deleteEventResult(resultId);
return { success: "Result deleted successfully" };
} catch (error) {
console.error("Error deleting result:", error);
return { error: "Failed to delete result" };
}
}
if (intent === "update-standings") {
// Bulk update season standings for final_standings events
try {
// Parse all form fields and collect participants with points
const participantPoints: Array<{ participantId: string; points: number }> = [];
for (const [key, value] of formData.entries()) {
if (key.startsWith("points-")) {
const participantId = key.replace("points-", "");
const points = value ? parseFloat(value as string) : 0;
if (points > 0) {
participantPoints.push({ participantId, points });
}
}
}
// Sort by points descending to determine positions
participantPoints.sort((a, b) => b.points - a.points);
// Assign positions based on sorted order and update
for (let i = 0; i < participantPoints.length; i++) {
const { participantId, points } = participantPoints[i];
const position = i + 1; // Position is 1-based
await upsertParticipantSeasonResult(
{
participantId,
sportsSeasonId: params.id,
currentPoints: points,
currentPosition: position,
}
);
}
// Also update participants with 0 or no points (they don't have a position)
for (const [key, value] of formData.entries()) {
if (key.startsWith("points-")) {
const participantId = key.replace("points-", "");
const points = value ? parseFloat(value as string) : 0;
if (points === 0) {
await upsertParticipantSeasonResult(
{
participantId,
sportsSeasonId: params.id,
currentPoints: 0,
currentPosition: undefined,
}
);
}
}
}
return { success: `Updated standings for ${participantPoints.length} participants (positions auto-calculated from points)` };
} catch (error) {
console.error("Error updating season standings:", error);
return { error: "Failed to update season standings" };
}
}
return { error: "Invalid action" };
}