203 lines
6 KiB
TypeScript
203 lines
6 KiB
TypeScript
import { data, type ActionFunctionArgs } from "react-router";
|
|
import { getAuth } from "@clerk/react-router/server";
|
|
import {
|
|
findSeasonById,
|
|
updateSeason,
|
|
updateSeasonStatus,
|
|
findTeamsBySeasonId,
|
|
isCommissioner,
|
|
createDraftPick,
|
|
initializeDraftTimers,
|
|
updateTeamTimer,
|
|
getSeasonTimers,
|
|
autoPickForTeam,
|
|
calculatePickInfo,
|
|
removeFromQueue,
|
|
getTeamQueue,
|
|
} from "~/models";
|
|
import { getSocketIO } from "../../../server/socket.js";
|
|
|
|
export async function action(args: ActionFunctionArgs) {
|
|
const { userId } = await getAuth(args);
|
|
if (!userId) {
|
|
return data({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
const { request } = args;
|
|
const formData = await request.formData();
|
|
const actionType = formData.get("action");
|
|
const seasonId = formData.get("seasonId");
|
|
|
|
if (!seasonId || typeof seasonId !== "string") {
|
|
return data({ error: "Season ID is required" }, { status: 400 });
|
|
}
|
|
|
|
// Get season
|
|
const season = await findSeasonById(seasonId);
|
|
if (!season) {
|
|
return data({ error: "Season not found" }, { status: 404 });
|
|
}
|
|
|
|
// Verify user is commissioner
|
|
const isUserCommissioner = await isCommissioner(season.leagueId, userId);
|
|
if (!isUserCommissioner) {
|
|
return data({ error: "Only commissioners can perform this action" }, { status: 403 });
|
|
}
|
|
|
|
try {
|
|
const io = getSocketIO();
|
|
|
|
switch (actionType) {
|
|
case "start-draft": {
|
|
// Check if draft already started
|
|
if (season.draftStartedAt) {
|
|
return data({ error: "Draft has already started" }, { status: 400 });
|
|
}
|
|
|
|
// Get teams
|
|
const teams = await findTeamsBySeasonId(seasonId);
|
|
|
|
// Initialize timers
|
|
await initializeDraftTimers(seasonId, teams, season.draftInitialTime || 120);
|
|
|
|
// Update season
|
|
await updateSeason(seasonId, {
|
|
draftStartedAt: new Date(),
|
|
currentPickNumber: 1,
|
|
draftPaused: false,
|
|
});
|
|
await updateSeasonStatus(seasonId, "draft");
|
|
|
|
// Get updated timers
|
|
const timers = await getSeasonTimers(seasonId);
|
|
|
|
// Broadcast to all clients
|
|
io.to(`draft-${seasonId}`).emit("draft-started", {
|
|
seasonId,
|
|
startedAt: new Date(),
|
|
timers,
|
|
});
|
|
|
|
return data({ success: true });
|
|
}
|
|
|
|
case "pause-draft": {
|
|
const pause = formData.get("pause") === "true";
|
|
|
|
// Check if draft has started
|
|
if (!season.draftStartedAt) {
|
|
return data({ error: "Draft has not started yet" }, { status: 400 });
|
|
}
|
|
|
|
// Update season
|
|
await updateSeason(seasonId, { draftPaused: pause });
|
|
|
|
// Broadcast to all clients
|
|
io.to(`draft-${seasonId}`).emit("draft-paused", {
|
|
seasonId,
|
|
isPaused: pause,
|
|
});
|
|
|
|
return data({ success: true });
|
|
}
|
|
|
|
case "force-pick": {
|
|
const teamId = formData.get("teamId");
|
|
|
|
if (!teamId || typeof teamId !== "string") {
|
|
return data({ error: "Team ID is required" }, { status: 400 });
|
|
}
|
|
|
|
// Check if draft has started
|
|
if (!season.draftStartedAt) {
|
|
return data({ error: "Draft has not started" }, { status: 400 });
|
|
}
|
|
|
|
// Check if draft is complete
|
|
const teams = await findTeamsBySeasonId(seasonId);
|
|
const totalPicks = season.draftRounds * teams.length;
|
|
const currentPickNumber = season.currentPickNumber || 1;
|
|
|
|
if (currentPickNumber > totalPicks) {
|
|
return data({ error: "Draft is already complete" }, { status: 400 });
|
|
}
|
|
|
|
// Auto-pick for the team
|
|
const participantId = await autoPickForTeam(seasonId, teamId);
|
|
|
|
if (!participantId) {
|
|
return data({ error: "No available participants to pick" }, { status: 400 });
|
|
}
|
|
|
|
// Calculate pick info
|
|
const { round, pickInRound } = calculatePickInfo(currentPickNumber, teams.length);
|
|
|
|
// Create the draft pick
|
|
const pick = await createDraftPick({
|
|
seasonId,
|
|
teamId,
|
|
participantId,
|
|
pickNumber: currentPickNumber,
|
|
round,
|
|
pickInRound,
|
|
pickedByUserId: userId,
|
|
pickedByType: "commissioner",
|
|
timeUsed: 0,
|
|
});
|
|
|
|
// Remove from queue if it was queued
|
|
const queue = await getTeamQueue(teamId);
|
|
const queueItem = queue.find((item: any) => item.participantId === participantId);
|
|
if (queueItem) {
|
|
await removeFromQueue(queueItem.id);
|
|
}
|
|
|
|
// Update season to next pick
|
|
const nextPickNumber = currentPickNumber + 1;
|
|
await updateSeason(seasonId, { currentPickNumber: nextPickNumber });
|
|
|
|
// Add increment time to current team's timer
|
|
if (season.draftIncrementTime) {
|
|
const timers = await getSeasonTimers(seasonId);
|
|
const currentTimer = timers.find((t: any) => t.teamId === teamId);
|
|
if (currentTimer) {
|
|
const newTime = currentTimer.timeRemaining + season.draftIncrementTime;
|
|
await updateTeamTimer(teamId, newTime);
|
|
}
|
|
}
|
|
|
|
// Check if draft is complete
|
|
if (nextPickNumber > totalPicks) {
|
|
await updateSeasonStatus(seasonId, "active");
|
|
io.to(`draft-${seasonId}`).emit("draft-completed", {
|
|
seasonId,
|
|
});
|
|
}
|
|
|
|
// Broadcast pick to all clients in the room
|
|
io.to(`draft-${seasonId}`).emit("pick-made", {
|
|
pick,
|
|
nextPickNumber,
|
|
isDraftComplete: nextPickNumber > totalPicks,
|
|
});
|
|
|
|
// Emit queue update to team owner
|
|
const updatedQueue = await getTeamQueue(teamId);
|
|
io.to(`draft-${seasonId}`).emit("queue-updated", {
|
|
teamId,
|
|
queue: updatedQueue,
|
|
});
|
|
|
|
return data({ success: true, pick });
|
|
}
|
|
|
|
default:
|
|
return data({ error: "Invalid action" }, { status: 400 });
|
|
}
|
|
} catch (error) {
|
|
console.error("Commissioner action error:", error);
|
|
return data({
|
|
error: error instanceof Error ? error.message : "Failed to perform action"
|
|
}, { status: 500 });
|
|
}
|
|
}
|