brackt/app/models/draft-queue.ts
Chris Parsons 16aa450d63
feat: proactively prune ineligible queue items after each pick (#59)
After every pick, recalculate draft eligibility for all teams and
remove any queued participants whose sport is no longer eligible
(e.g. a team queued a snooker player but just filled their last flex
slot). Previously this was only caught lazily when autodraft fired,
which could pause the draft or pick an unwanted player.

- Add getAllQueuesForSeason to draft-queue.ts — fetches all queue rows
  for a season in one query (Map<teamId, QueueItem[]>) instead of N+1
  per-team queries
- Add pruneIneligibleQueueItems to draft-utils.ts — uses Promise.all
  for the four required data fetches, collects ineligible items in a
  single loop pass, warns on orphaned participant references
- Call from both pick paths: executeAutoPick and draft.make-pick.ts
- Emit queue-eligibility-pruned socket event per affected team so the
  client updates the queue UI in real time
- Add 5 tests covering: single ineligible removal, all eligible (no-op),
  empty queues (no delete called, getTeamQueue never called), mixed
  queue (only ineligible item removed), and unknown participant (warn)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 22:07:22 -08:00

117 lines
3.2 KiB
TypeScript

import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq, and, asc } from "drizzle-orm";
export async function addToQueue(data: {
seasonId: string;
teamId: string;
participantId: string;
queuePosition: number;
}) {
const db = database();
const [item] = await db.insert(schema.draftQueue).values(data).returning();
return item;
}
export async function removeFromQueue(queueId: string) {
const db = database();
await db.delete(schema.draftQueue).where(eq(schema.draftQueue.id, queueId));
}
export async function getTeamQueue(teamId: string, providedDb?: ReturnType<typeof database>) {
const db = providedDb || database();
return await db
.select()
.from(schema.draftQueue)
.where(eq(schema.draftQueue.teamId, teamId))
.orderBy(asc(schema.draftQueue.queuePosition));
}
export async function isParticipantInQueue(teamId: string, participantId: string): Promise<boolean> {
const db = database();
const existing = await db.query.draftQueue.findFirst({
where: and(
eq(schema.draftQueue.teamId, teamId),
eq(schema.draftQueue.participantId, participantId)
),
});
return !!existing;
}
export async function clearTeamQueue(teamId: string) {
const db = database();
await db.delete(schema.draftQueue).where(eq(schema.draftQueue.teamId, teamId));
}
export async function reorderQueue(teamId: string, participantIds: string[]) {
// Delete existing queue
await clearTeamQueue(teamId);
// Get season ID from first queue item (or we need to pass it)
// For now, we'll need to pass seasonId
return participantIds;
}
export async function reorderQueueWithSeason(
seasonId: string,
teamId: string,
participantIds: string[]
) {
// Delete existing queue
await clearTeamQueue(teamId);
// Insert new queue in order
const items = participantIds.map((participantId, index) => ({
seasonId,
teamId,
participantId,
queuePosition: index + 1,
}));
if (items.length > 0) {
const db = database();
await db.insert(schema.draftQueue).values(items);
}
}
export async function removeParticipantFromQueue(teamId: string, participantId: string) {
const db = database();
await db
.delete(schema.draftQueue)
.where(
and(
eq(schema.draftQueue.teamId, teamId),
eq(schema.draftQueue.participantId, participantId)
)
);
}
export async function getAllQueuesForSeason(
seasonId: string,
providedDb?: ReturnType<typeof database>
): Promise<Map<string, typeof schema.draftQueue.$inferSelect[]>> {
const db = providedDb || database();
const rows = await db
.select()
.from(schema.draftQueue)
.where(eq(schema.draftQueue.seasonId, seasonId))
.orderBy(asc(schema.draftQueue.queuePosition));
const byTeam = new Map<string, typeof schema.draftQueue.$inferSelect[]>();
for (const row of rows) {
const existing = byTeam.get(row.teamId);
if (existing) {
existing.push(row);
} else {
byTeam.set(row.teamId, [row]);
}
}
return byTeam;
}
export async function clearAllQueuesForSeason(seasonId: string) {
const db = database();
await db
.delete(schema.draftQueue)
.where(eq(schema.draftQueue.seasonId, seasonId));
}