brackt/app/routes/api/draft.make-pick.ts

267 lines
8.2 KiB
TypeScript
Raw Normal View History

import { getAuth } from "@clerk/react-router/server";
import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq, and } from "drizzle-orm";
import { calculateDraftEligibility } from "~/lib/draft-eligibility";
import { getDraftPicksWithSports, getTeamDraftPicksWithSports } from "~/models/draft-pick";
import { getParticipantsForSeasonWithSports } from "~/models/participant";
import { getSeasonSportsSimple } from "~/models/season-sport";
Improve draft room UX with better error handling and UI refinements (#17) * Remove pause/resume controls when draft is complete, rename Live to Connected - Hide Pause/Resume Draft buttons when isDraftComplete is true - Change 'Live' status indicator to 'Connected' in both draft room and draft board views https://claude.ai/code/session_01AUaKzx465NrY29Qv6MVwjC * Fix code review issues in draft room: security, bugs, and quality Security: - Fix inconsistent commissioner check: draft.start, force-autopick, force-manual-pick, and make-pick all now query the commissioners table instead of league.createdBy, so co-commissioners have consistent access to all draft controls Bugs: - canPick now includes !isPaused so the UI correctly blocks picks during a pause - isDraftComplete initial state now covers 'completed' season status, not just 'active' - Guard JSON.parse in queue.reorder.ts with try/catch to return 400 instead of 500 Code quality: - Add error handling (try/catch + toast) to handlePauseDraft, handleResumeDraft, handleRemoveFromQueue, and handleReorderQueue - Replace alert() with toast.error() in handleMakePick, handleForceAutopick, handleForceManualPick for consistent UX - Memoize filteredParticipants with useMemo to avoid recomputing on every render - Replace custom force-pick dialog div with ShadCN Dialog component for proper keyboard support (Escape to close, focus trap, accessible markup); add dialog.tsx - Remove console.log debug statements from socket event handlers and API routes - Replace (global as any).__socketIO with getSocketIO() across all API routes - Replace window.location.reload() in handleStartDraft with useRevalidator https://claude.ai/code/session_01AUaKzx465NrY29Qv6MVwjC --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 21:50:27 -08:00
import { getSocketIO } from "../../../server/socket";
Claude/fix pick timer ghll n (#29) * Fix force-manual-pick resetting next team's timer to initial time When a commissioner forced a manual pick, the next team's timer was being reset to the initial time (2 minutes) instead of carrying forward their existing time bank balance. This aligns force-manual-pick with the behavior of regular user picks and force-autopick: the picking team gets their increment added, and the next team's timer is left untouched so their bank carries forward. https://claude.ai/code/session_01X7gwWmafUSEvVHcV7Raz5p * Add regression tests for draft.force-manual-pick timer behavior 18 tests across 5 describe blocks covering: - Authorization (401/403) - Input validation (missing fields, bad participant, ineligible sport) - Successful pick (response shape, draft-complete detection, socket events) - Timer behavior (increment added to picking team, new timer creation, additive not reset) - Two regression tests confirming the next team's timer is never touched: draftTimers.findFirst called exactly once, no timer-update emitted for next team, db.update called exactly twice (not three times) https://claude.ai/code/session_01X7gwWmafUSEvVHcV7Raz5p * Add TypeScript types and improve draft validation (#28) * Code review fixes: type safety, security hardening, and dead code removal - Fix Socket.IO event types: draft-paused and draft-resumed were typed as () => void but are emitted with { seasonId, paused } data payloads - Fix draft.force-manual-pick: add missing season.status === "draft" guard so commissioners cannot force picks outside an active draft; add duplicate pick-number check so a slot cannot be assigned two picks (the previous code only checked participant uniqueness, not slot uniqueness) - Replace args: any with ActionFunctionArgs / Route.LoaderArgs across all API routes and league loaders; replace (auth as any).userId casts with proper const { userId } = await getAuth(args) destructuring - Remove unused isSnakeDraft = true dead variable from draft.make-pick - Replace autodraftSettings: any and draftSlots: any[] in draft-utils with properly typed InferSelectModel / DraftSlot types - Update force-manual-pick tests: sequence draftPicks.findFirst mock for the two-call flow; add new tests for status-check and slot-uniqueness https://claude.ai/code/session_01FKq2gPFYpgdfxr8cw4Z2AZ * Fix RouterContextProvider type errors in action test files Cast context argument to RouterContextProvider in test helpers so ActionFunctionArgs strict typing is satisfied without weakening the production action signatures back to any. https://claude.ai/code/session_01FKq2gPFYpgdfxr8cw4Z2AZ --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-22 19:29:29 -08:00
import type { ActionFunctionArgs } from "react-router";
export async function action(args: ActionFunctionArgs) {
const { request } = args;
Claude/fix pick timer ghll n (#29) * Fix force-manual-pick resetting next team's timer to initial time When a commissioner forced a manual pick, the next team's timer was being reset to the initial time (2 minutes) instead of carrying forward their existing time bank balance. This aligns force-manual-pick with the behavior of regular user picks and force-autopick: the picking team gets their increment added, and the next team's timer is left untouched so their bank carries forward. https://claude.ai/code/session_01X7gwWmafUSEvVHcV7Raz5p * Add regression tests for draft.force-manual-pick timer behavior 18 tests across 5 describe blocks covering: - Authorization (401/403) - Input validation (missing fields, bad participant, ineligible sport) - Successful pick (response shape, draft-complete detection, socket events) - Timer behavior (increment added to picking team, new timer creation, additive not reset) - Two regression tests confirming the next team's timer is never touched: draftTimers.findFirst called exactly once, no timer-update emitted for next team, db.update called exactly twice (not three times) https://claude.ai/code/session_01X7gwWmafUSEvVHcV7Raz5p * Add TypeScript types and improve draft validation (#28) * Code review fixes: type safety, security hardening, and dead code removal - Fix Socket.IO event types: draft-paused and draft-resumed were typed as () => void but are emitted with { seasonId, paused } data payloads - Fix draft.force-manual-pick: add missing season.status === "draft" guard so commissioners cannot force picks outside an active draft; add duplicate pick-number check so a slot cannot be assigned two picks (the previous code only checked participant uniqueness, not slot uniqueness) - Replace args: any with ActionFunctionArgs / Route.LoaderArgs across all API routes and league loaders; replace (auth as any).userId casts with proper const { userId } = await getAuth(args) destructuring - Remove unused isSnakeDraft = true dead variable from draft.make-pick - Replace autodraftSettings: any and draftSlots: any[] in draft-utils with properly typed InferSelectModel / DraftSlot types - Update force-manual-pick tests: sequence draftPicks.findFirst mock for the two-call flow; add new tests for status-check and slot-uniqueness https://claude.ai/code/session_01FKq2gPFYpgdfxr8cw4Z2AZ * Fix RouterContextProvider type errors in action test files Cast context argument to RouterContextProvider in test helpers so ActionFunctionArgs strict typing is satisfied without weakening the production action signatures back to any. https://claude.ai/code/session_01FKq2gPFYpgdfxr8cw4Z2AZ --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-22 19:29:29 -08:00
const { userId } = await getAuth(args);
if (!userId) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
const formData = await request.formData();
const seasonId = formData.get("seasonId") as string;
const participantId = formData.get("participantId") as string;
if (!seasonId || !participantId) {
return Response.json({ error: "Missing required fields" }, { status: 400 });
}
const db = database();
// Get season details
const season = await db.query.seasons.findFirst({
where: eq(schema.seasons.id, seasonId),
});
if (!season) {
return Response.json({ error: "Season not found" }, { status: 404 });
}
if (season.status !== "draft") {
return Response.json({ error: "Draft is not currently active" }, { status: 400 });
}
if (season.draftPaused) {
return Response.json({ error: "Draft is currently paused" }, { status: 400 });
}
// Get current draft slot (who should be picking now)
const currentPickNumber = season.currentPickNumber || 1;
const draftSlots = await db.query.draftSlots.findMany({
where: eq(schema.draftSlots.seasonId, seasonId),
orderBy: schema.draftSlots.draftOrder,
with: {
team: true,
},
});
const totalTeams = draftSlots.length;
const currentRound = Math.ceil(currentPickNumber / totalTeams);
const isEvenRound = currentRound % 2 === 0;
Claude/fix pick timer ghll n (#29) * Fix force-manual-pick resetting next team's timer to initial time When a commissioner forced a manual pick, the next team's timer was being reset to the initial time (2 minutes) instead of carrying forward their existing time bank balance. This aligns force-manual-pick with the behavior of regular user picks and force-autopick: the picking team gets their increment added, and the next team's timer is left untouched so their bank carries forward. https://claude.ai/code/session_01X7gwWmafUSEvVHcV7Raz5p * Add regression tests for draft.force-manual-pick timer behavior 18 tests across 5 describe blocks covering: - Authorization (401/403) - Input validation (missing fields, bad participant, ineligible sport) - Successful pick (response shape, draft-complete detection, socket events) - Timer behavior (increment added to picking team, new timer creation, additive not reset) - Two regression tests confirming the next team's timer is never touched: draftTimers.findFirst called exactly once, no timer-update emitted for next team, db.update called exactly twice (not three times) https://claude.ai/code/session_01X7gwWmafUSEvVHcV7Raz5p * Add TypeScript types and improve draft validation (#28) * Code review fixes: type safety, security hardening, and dead code removal - Fix Socket.IO event types: draft-paused and draft-resumed were typed as () => void but are emitted with { seasonId, paused } data payloads - Fix draft.force-manual-pick: add missing season.status === "draft" guard so commissioners cannot force picks outside an active draft; add duplicate pick-number check so a slot cannot be assigned two picks (the previous code only checked participant uniqueness, not slot uniqueness) - Replace args: any with ActionFunctionArgs / Route.LoaderArgs across all API routes and league loaders; replace (auth as any).userId casts with proper const { userId } = await getAuth(args) destructuring - Remove unused isSnakeDraft = true dead variable from draft.make-pick - Replace autodraftSettings: any and draftSlots: any[] in draft-utils with properly typed InferSelectModel / DraftSlot types - Update force-manual-pick tests: sequence draftPicks.findFirst mock for the two-call flow; add new tests for status-check and slot-uniqueness https://claude.ai/code/session_01FKq2gPFYpgdfxr8cw4Z2AZ * Fix RouterContextProvider type errors in action test files Cast context argument to RouterContextProvider in test helpers so ActionFunctionArgs strict typing is satisfied without weakening the production action signatures back to any. https://claude.ai/code/session_01FKq2gPFYpgdfxr8cw4Z2AZ --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-22 19:29:29 -08:00
// Calculate which team should pick (snake draft)
let pickInRound = ((currentPickNumber - 1) % totalTeams) + 1;
Claude/fix pick timer ghll n (#29) * Fix force-manual-pick resetting next team's timer to initial time When a commissioner forced a manual pick, the next team's timer was being reset to the initial time (2 minutes) instead of carrying forward their existing time bank balance. This aligns force-manual-pick with the behavior of regular user picks and force-autopick: the picking team gets their increment added, and the next team's timer is left untouched so their bank carries forward. https://claude.ai/code/session_01X7gwWmafUSEvVHcV7Raz5p * Add regression tests for draft.force-manual-pick timer behavior 18 tests across 5 describe blocks covering: - Authorization (401/403) - Input validation (missing fields, bad participant, ineligible sport) - Successful pick (response shape, draft-complete detection, socket events) - Timer behavior (increment added to picking team, new timer creation, additive not reset) - Two regression tests confirming the next team's timer is never touched: draftTimers.findFirst called exactly once, no timer-update emitted for next team, db.update called exactly twice (not three times) https://claude.ai/code/session_01X7gwWmafUSEvVHcV7Raz5p * Add TypeScript types and improve draft validation (#28) * Code review fixes: type safety, security hardening, and dead code removal - Fix Socket.IO event types: draft-paused and draft-resumed were typed as () => void but are emitted with { seasonId, paused } data payloads - Fix draft.force-manual-pick: add missing season.status === "draft" guard so commissioners cannot force picks outside an active draft; add duplicate pick-number check so a slot cannot be assigned two picks (the previous code only checked participant uniqueness, not slot uniqueness) - Replace args: any with ActionFunctionArgs / Route.LoaderArgs across all API routes and league loaders; replace (auth as any).userId casts with proper const { userId } = await getAuth(args) destructuring - Remove unused isSnakeDraft = true dead variable from draft.make-pick - Replace autodraftSettings: any and draftSlots: any[] in draft-utils with properly typed InferSelectModel / DraftSlot types - Update force-manual-pick tests: sequence draftPicks.findFirst mock for the two-call flow; add new tests for status-check and slot-uniqueness https://claude.ai/code/session_01FKq2gPFYpgdfxr8cw4Z2AZ * Fix RouterContextProvider type errors in action test files Cast context argument to RouterContextProvider in test helpers so ActionFunctionArgs strict typing is satisfied without weakening the production action signatures back to any. https://claude.ai/code/session_01FKq2gPFYpgdfxr8cw4Z2AZ --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-22 19:29:29 -08:00
if (isEvenRound) {
pickInRound = totalTeams - pickInRound + 1;
}
const currentDraftSlot = draftSlots.find((slot) => slot.draftOrder === pickInRound);
if (!currentDraftSlot) {
return Response.json({ error: "Invalid draft state" }, { status: 500 });
}
// Check permissions: must be team owner or commissioner
const isTeamOwner = currentDraftSlot.team.ownerId === userId;
Improve draft room UX with better error handling and UI refinements (#17) * Remove pause/resume controls when draft is complete, rename Live to Connected - Hide Pause/Resume Draft buttons when isDraftComplete is true - Change 'Live' status indicator to 'Connected' in both draft room and draft board views https://claude.ai/code/session_01AUaKzx465NrY29Qv6MVwjC * Fix code review issues in draft room: security, bugs, and quality Security: - Fix inconsistent commissioner check: draft.start, force-autopick, force-manual-pick, and make-pick all now query the commissioners table instead of league.createdBy, so co-commissioners have consistent access to all draft controls Bugs: - canPick now includes !isPaused so the UI correctly blocks picks during a pause - isDraftComplete initial state now covers 'completed' season status, not just 'active' - Guard JSON.parse in queue.reorder.ts with try/catch to return 400 instead of 500 Code quality: - Add error handling (try/catch + toast) to handlePauseDraft, handleResumeDraft, handleRemoveFromQueue, and handleReorderQueue - Replace alert() with toast.error() in handleMakePick, handleForceAutopick, handleForceManualPick for consistent UX - Memoize filteredParticipants with useMemo to avoid recomputing on every render - Replace custom force-pick dialog div with ShadCN Dialog component for proper keyboard support (Escape to close, focus trap, accessible markup); add dialog.tsx - Remove console.log debug statements from socket event handlers and API routes - Replace (global as any).__socketIO with getSocketIO() across all API routes - Replace window.location.reload() in handleStartDraft with useRevalidator https://claude.ai/code/session_01AUaKzx465NrY29Qv6MVwjC --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 21:50:27 -08:00
const commissionerRecord = await db.query.commissioners.findFirst({
where: and(
eq(schema.commissioners.leagueId, season.leagueId),
eq(schema.commissioners.userId, userId)
),
});
const isCommissioner = !!commissionerRecord;
if (!isTeamOwner && !isCommissioner) {
return Response.json({ error: "Not your turn to pick" }, { status: 403 });
}
// Check if participant is already drafted
const existingPick = await db.query.draftPicks.findFirst({
where: and(
eq(schema.draftPicks.seasonId, seasonId),
eq(schema.draftPicks.participantId, participantId)
),
});
if (existingPick) {
return Response.json({ error: "Participant already drafted" }, { status: 400 });
}
// Get participant details
const participant = await db.query.participants.findFirst({
where: eq(schema.participants.id, participantId),
with: {
sportsSeason: {
with: {
sport: true,
},
},
},
});
if (!participant) {
return Response.json({ error: "Participant not found" }, { status: 404 });
}
// ELIGIBILITY VALIDATION: Check if team can draft from this sport
const allPicks = await getDraftPicksWithSports(seasonId);
const teamPicks = await getTeamDraftPicksWithSports(currentDraftSlot.teamId, seasonId);
const allParticipants = await getParticipantsForSeasonWithSports(seasonId);
const seasonSports = await getSeasonSportsSimple(seasonId);
// Get all teams for the season
const allTeams = draftSlots.map((slot) => ({ id: slot.teamId }));
const eligibility = calculateDraftEligibility(
currentDraftSlot.teamId,
teamPicks,
allPicks,
allParticipants,
seasonSports,
season.draftRounds,
allTeams
);
const sportId = participant.sportsSeason.sport.id;
if (!eligibility.eligibleSportIds.has(sportId)) {
const reason = eligibility.ineligibleReasons[sportId] || "Cannot draft from this sport";
return Response.json({ error: reason }, { status: 400 });
}
// Create the draft pick
const [draftPick] = await db
.insert(schema.draftPicks)
.values({
seasonId,
teamId: currentDraftSlot.teamId,
participantId,
pickNumber: currentPickNumber,
round: currentRound,
pickInRound,
pickedByUserId: userId,
pickedByType: isTeamOwner ? "owner" : "commissioner",
})
.returning();
// Remove from ALL team queues in this season (participant is now drafted)
await db
.delete(schema.draftQueue)
.where(
and(
eq(schema.draftQueue.seasonId, seasonId),
eq(schema.draftQueue.participantId, participantId)
)
);
// Notify all clients that this participant was removed from queues
try {
Improve draft room UX with better error handling and UI refinements (#17) * Remove pause/resume controls when draft is complete, rename Live to Connected - Hide Pause/Resume Draft buttons when isDraftComplete is true - Change 'Live' status indicator to 'Connected' in both draft room and draft board views https://claude.ai/code/session_01AUaKzx465NrY29Qv6MVwjC * Fix code review issues in draft room: security, bugs, and quality Security: - Fix inconsistent commissioner check: draft.start, force-autopick, force-manual-pick, and make-pick all now query the commissioners table instead of league.createdBy, so co-commissioners have consistent access to all draft controls Bugs: - canPick now includes !isPaused so the UI correctly blocks picks during a pause - isDraftComplete initial state now covers 'completed' season status, not just 'active' - Guard JSON.parse in queue.reorder.ts with try/catch to return 400 instead of 500 Code quality: - Add error handling (try/catch + toast) to handlePauseDraft, handleResumeDraft, handleRemoveFromQueue, and handleReorderQueue - Replace alert() with toast.error() in handleMakePick, handleForceAutopick, handleForceManualPick for consistent UX - Memoize filteredParticipants with useMemo to avoid recomputing on every render - Replace custom force-pick dialog div with ShadCN Dialog component for proper keyboard support (Escape to close, focus trap, accessible markup); add dialog.tsx - Remove console.log debug statements from socket event handlers and API routes - Replace (global as any).__socketIO with getSocketIO() across all API routes - Replace window.location.reload() in handleStartDraft with useRevalidator https://claude.ai/code/session_01AUaKzx465NrY29Qv6MVwjC --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 21:50:27 -08:00
getSocketIO().to(`draft-${seasonId}`).emit("participant-removed-from-queues", {
participantId,
});
} catch (error) {
console.error("Socket.IO participant-removed-from-queues error:", error);
}
// Calculate next pick info (before updating season)
const nextPickNumber = currentPickNumber + 1;
const totalPicks = totalTeams * season.draftRounds;
const isDraftComplete = nextPickNumber > totalPicks;
// Add increment to the team that just picked (post-pick reward)
const incrementTime = season.draftIncrementTime || 30;
const currentTimer = await db.query.draftTimers.findFirst({
where: and(
eq(schema.draftTimers.seasonId, seasonId),
eq(schema.draftTimers.teamId, currentDraftSlot.teamId)
),
});
const newTimeRemaining = (currentTimer?.timeRemaining ?? 0) + incrementTime;
if (currentTimer) {
await db
.update(schema.draftTimers)
.set({ timeRemaining: newTimeRemaining, updatedAt: new Date() })
.where(eq(schema.draftTimers.id, currentTimer.id));
} else {
await db.insert(schema.draftTimers).values({
seasonId,
teamId: currentDraftSlot.teamId,
timeRemaining: newTimeRemaining,
});
}
try {
getSocketIO().to(`draft-${seasonId}`).emit("timer-update", {
seasonId,
teamId: currentDraftSlot.teamId,
timeRemaining: newTimeRemaining,
currentPickNumber,
});
} catch (error) {
console.error("Socket.IO timer-update error:", error);
}
// Next team's timer is unchanged — their bank carries forward as-is
// (no emit needed; the timer system will start decrementing their existing bank)
// Update season's current pick number (AFTER initializing next timer to prevent race condition)
await db
.update(schema.seasons)
.set({
currentPickNumber: isDraftComplete ? currentPickNumber : nextPickNumber,
status: isDraftComplete ? "active" : season.status,
})
.where(eq(schema.seasons.id, seasonId));
// Emit socket event
try {
Improve draft room UX with better error handling and UI refinements (#17) * Remove pause/resume controls when draft is complete, rename Live to Connected - Hide Pause/Resume Draft buttons when isDraftComplete is true - Change 'Live' status indicator to 'Connected' in both draft room and draft board views https://claude.ai/code/session_01AUaKzx465NrY29Qv6MVwjC * Fix code review issues in draft room: security, bugs, and quality Security: - Fix inconsistent commissioner check: draft.start, force-autopick, force-manual-pick, and make-pick all now query the commissioners table instead of league.createdBy, so co-commissioners have consistent access to all draft controls Bugs: - canPick now includes !isPaused so the UI correctly blocks picks during a pause - isDraftComplete initial state now covers 'completed' season status, not just 'active' - Guard JSON.parse in queue.reorder.ts with try/catch to return 400 instead of 500 Code quality: - Add error handling (try/catch + toast) to handlePauseDraft, handleResumeDraft, handleRemoveFromQueue, and handleReorderQueue - Replace alert() with toast.error() in handleMakePick, handleForceAutopick, handleForceManualPick for consistent UX - Memoize filteredParticipants with useMemo to avoid recomputing on every render - Replace custom force-pick dialog div with ShadCN Dialog component for proper keyboard support (Escape to close, focus trap, accessible markup); add dialog.tsx - Remove console.log debug statements from socket event handlers and API routes - Replace (global as any).__socketIO with getSocketIO() across all API routes - Replace window.location.reload() in handleStartDraft with useRevalidator https://claude.ai/code/session_01AUaKzx465NrY29Qv6MVwjC --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 21:50:27 -08:00
getSocketIO().to(`draft-${seasonId}`).emit("pick-made", {
pick: {
...draftPick,
team: currentDraftSlot.team,
participant: {
...participant,
sport: participant.sportsSeason.sport,
},
sport: participant.sportsSeason.sport,
},
nextPickNumber: isDraftComplete ? currentPickNumber : nextPickNumber,
isDraftComplete,
});
if (isDraftComplete) {
Improve draft room UX with better error handling and UI refinements (#17) * Remove pause/resume controls when draft is complete, rename Live to Connected - Hide Pause/Resume Draft buttons when isDraftComplete is true - Change 'Live' status indicator to 'Connected' in both draft room and draft board views https://claude.ai/code/session_01AUaKzx465NrY29Qv6MVwjC * Fix code review issues in draft room: security, bugs, and quality Security: - Fix inconsistent commissioner check: draft.start, force-autopick, force-manual-pick, and make-pick all now query the commissioners table instead of league.createdBy, so co-commissioners have consistent access to all draft controls Bugs: - canPick now includes !isPaused so the UI correctly blocks picks during a pause - isDraftComplete initial state now covers 'completed' season status, not just 'active' - Guard JSON.parse in queue.reorder.ts with try/catch to return 400 instead of 500 Code quality: - Add error handling (try/catch + toast) to handlePauseDraft, handleResumeDraft, handleRemoveFromQueue, and handleReorderQueue - Replace alert() with toast.error() in handleMakePick, handleForceAutopick, handleForceManualPick for consistent UX - Memoize filteredParticipants with useMemo to avoid recomputing on every render - Replace custom force-pick dialog div with ShadCN Dialog component for proper keyboard support (Escape to close, focus trap, accessible markup); add dialog.tsx - Remove console.log debug statements from socket event handlers and API routes - Replace (global as any).__socketIO with getSocketIO() across all API routes - Replace window.location.reload() in handleStartDraft with useRevalidator https://claude.ai/code/session_01AUaKzx465NrY29Qv6MVwjC --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 21:50:27 -08:00
getSocketIO().to(`draft-${seasonId}`).emit("draft-completed");
}
} catch (error) {
console.error("Socket.IO error:", error);
}
// Check if next team has autodraft enabled and trigger immediately
if (!isDraftComplete) {
const { checkAndTriggerNextAutodraft } = await import("~/models/draft-utils");
await checkAndTriggerNextAutodraft({
seasonId,
nextPickNumber,
totalTeams,
draftSlots,
db,
});
}
return Response.json({
success: true,
pick: draftPick,
nextPickNumber: isDraftComplete ? currentPickNumber : nextPickNumber,
isDraftComplete,
});
}