* Fix draft order showing only new team when league size increased before order was set When a commissioner increased the team count on a league that had teams but no draft order set yet, the server created draft slots only for the newly-added teams. This left the DB with N+K teams but only K slots, causing the drag-and-drop list to display only the K new teams. Two fixes: 1. Server: only append new draft slots when an order was already set (existingSlots.length > 0). If no order exists yet, skip slot creation so the page correctly treats the order as unset for all teams. 2. Frontend: buildDraftOrderTeams() appends any unslotted teams after the slotted ones, so a partially-corrupt DB state still shows all teams. https://claude.ai/code/session_01M3H55gnMxRztJK9KMXqqZo * Address code review feedback on draft order bug fix - Move buildDraftOrderTeams to app/lib/draft-order.ts so it is importable and testable; remove the local copy from the settings component - Add unit tests for buildDraftOrderTeams covering the empty, full, and partial-slot cases - Tighten the draft slot guard from existingSlots.length > 0 to existingSlots.length === currentTeamCount so partial legacy states are treated the same as "order not set" - Condense the 3-line server comment to a single line per project style - Rename getNumTeamsInSeason → getNumDraftSlotsBySeasonId to reflect what the function actually counts, and update its one call site - Add tests for the server-side slot-creation guard logic https://claude.ai/code/session_01M3H55gnMxRztJK9KMXqqZo * Fix lint: move shouldAppendDraftSlots to outer scope oxlint (consistent-function-scoping) requires functions that don't close over any variables to be defined at the outer scope rather than inside a describe block. https://claude.ai/code/session_01M3H55gnMxRztJK9KMXqqZo --------- Co-authored-by: Claude <noreply@anthropic.com>
160 lines
3.9 KiB
TypeScript
160 lines
3.9 KiB
TypeScript
import { eq, count } from "drizzle-orm";
|
|
import { database } from "~/database/context";
|
|
import * as schema from "~/database/schema";
|
|
import type { RawFlagConfig } from "~/lib/flag-types";
|
|
|
|
export type DraftSlot = typeof schema.draftSlots.$inferSelect;
|
|
export type NewDraftSlot = typeof schema.draftSlots.$inferInsert;
|
|
|
|
export interface DraftSlotWithTeam extends DraftSlot {
|
|
team: {
|
|
id: string;
|
|
name: string;
|
|
ownerId: string | null;
|
|
logoUrl?: string | null;
|
|
flagConfig?: RawFlagConfig | null;
|
|
avatarType?: string | null;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create a single draft slot
|
|
*/
|
|
export async function createDraftSlot(data: NewDraftSlot): Promise<DraftSlot> {
|
|
const db = database();
|
|
const [draftSlot] = await db
|
|
.insert(schema.draftSlots)
|
|
.values(data)
|
|
.returning();
|
|
return draftSlot;
|
|
}
|
|
|
|
/**
|
|
* Create multiple draft slots at once
|
|
*/
|
|
export async function createManyDraftSlots(
|
|
slots: NewDraftSlot[]
|
|
): Promise<DraftSlot[]> {
|
|
const db = database();
|
|
return await db.insert(schema.draftSlots).values(slots).returning();
|
|
}
|
|
|
|
/**
|
|
* Find all draft slots for a season, ordered by draft order
|
|
*/
|
|
export async function findDraftSlotsBySeasonId(
|
|
seasonId: string
|
|
): Promise<DraftSlotWithTeam[]> {
|
|
const db = database();
|
|
return await db.query.draftSlots.findMany({
|
|
where: eq(schema.draftSlots.seasonId, seasonId),
|
|
orderBy: (draftSlots, { asc }) => [asc(draftSlots.draftOrder)],
|
|
with: {
|
|
team: {
|
|
columns: {
|
|
id: true,
|
|
name: true,
|
|
ownerId: true,
|
|
logoUrl: true,
|
|
flagConfig: true,
|
|
avatarType: true,
|
|
},
|
|
},
|
|
},
|
|
}) as DraftSlotWithTeam[];
|
|
}
|
|
|
|
/**
|
|
* Find a draft slot by team ID
|
|
*/
|
|
export async function findDraftSlotByTeamId(
|
|
teamId: string
|
|
): Promise<DraftSlot | undefined> {
|
|
const db = database();
|
|
return await db.query.draftSlots.findFirst({
|
|
where: eq(schema.draftSlots.teamId, teamId),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update a draft slot's order
|
|
*/
|
|
export async function updateDraftSlotOrder(
|
|
id: string,
|
|
draftOrder: number
|
|
): Promise<DraftSlot> {
|
|
const db = database();
|
|
const [draftSlot] = await db
|
|
.update(schema.draftSlots)
|
|
.set({ draftOrder, updatedAt: new Date() })
|
|
.where(eq(schema.draftSlots.id, id))
|
|
.returning();
|
|
return draftSlot;
|
|
}
|
|
|
|
/**
|
|
* Delete all draft slots for a season
|
|
*/
|
|
export async function deleteDraftSlotsBySeasonId(
|
|
seasonId: string
|
|
): Promise<void> {
|
|
const db = database();
|
|
await db
|
|
.delete(schema.draftSlots)
|
|
.where(eq(schema.draftSlots.seasonId, seasonId));
|
|
}
|
|
|
|
/**
|
|
* Delete a specific draft slot
|
|
*/
|
|
export async function deleteDraftSlot(id: string): Promise<void> {
|
|
const db = database();
|
|
await db.delete(schema.draftSlots).where(eq(schema.draftSlots.id, id));
|
|
}
|
|
|
|
/**
|
|
* Set the draft order for all teams in a season
|
|
* This will delete existing slots and create new ones
|
|
*/
|
|
export async function setDraftOrder(
|
|
seasonId: string,
|
|
teamIds: string[]
|
|
): Promise<DraftSlot[]> {
|
|
// Delete existing draft slots for this season
|
|
await deleteDraftSlotsBySeasonId(seasonId);
|
|
|
|
// Create new draft slots with the specified order
|
|
const slots = teamIds.map((teamId, index) => ({
|
|
seasonId,
|
|
teamId,
|
|
draftOrder: index + 1,
|
|
}));
|
|
|
|
return await createManyDraftSlots(slots);
|
|
}
|
|
|
|
export async function getNumDraftSlotsBySeasonId(seasonId: string): Promise<number> {
|
|
const db = database();
|
|
const [result] = await db
|
|
.select({ count: count() })
|
|
.from(schema.draftSlots)
|
|
.where(eq(schema.draftSlots.seasonId, seasonId));
|
|
return result?.count ?? 0;
|
|
}
|
|
|
|
/**
|
|
* Randomize the draft order for a season
|
|
*/
|
|
export async function randomizeDraftOrder(
|
|
seasonId: string,
|
|
teamIds: string[]
|
|
): Promise<DraftSlot[]> {
|
|
// Shuffle the team IDs using Fisher-Yates algorithm
|
|
const shuffled = [...teamIds];
|
|
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
}
|
|
|
|
return await setDraftOrder(seasonId, shuffled);
|
|
}
|