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
This commit is contained in:
parent
3da27af036
commit
c036cf6e28
21 changed files with 116 additions and 64 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
import { database } from "~/database/context";
|
import { database } from "~/database/context";
|
||||||
import * as schema from "~/database/schema";
|
import * as schema from "~/database/schema";
|
||||||
import { eq, and, notInArray, desc, inArray } from "drizzle-orm";
|
import { eq, and, notInArray, desc, inArray } from "drizzle-orm";
|
||||||
|
import type { InferSelectModel } from "drizzle-orm";
|
||||||
import { getTeamQueue } from "./draft-queue";
|
import { getTeamQueue } from "./draft-queue";
|
||||||
import { isParticipantDrafted, getDraftPicksWithSports, getTeamDraftPicksWithSports } from "./draft-pick";
|
import { isParticipantDrafted, getDraftPicksWithSports, getTeamDraftPicksWithSports } from "./draft-pick";
|
||||||
import { getParticipantsForSeasonWithSports } from "./participant";
|
import { getParticipantsForSeasonWithSports } from "./participant";
|
||||||
|
|
@ -12,11 +13,13 @@ import { getSocketIO } from "../../server/socket";
|
||||||
* Check if the next team has autodraft enabled and immediately execute their pick
|
* Check if the next team has autodraft enabled and immediately execute their pick
|
||||||
* This is called after a pick is made to chain autodraft picks
|
* This is called after a pick is made to chain autodraft picks
|
||||||
*/
|
*/
|
||||||
|
type DraftSlot = { teamId: string; draftOrder: number };
|
||||||
|
|
||||||
export async function checkAndTriggerNextAutodraft(params: {
|
export async function checkAndTriggerNextAutodraft(params: {
|
||||||
seasonId: string;
|
seasonId: string;
|
||||||
nextPickNumber: number;
|
nextPickNumber: number;
|
||||||
totalTeams: number;
|
totalTeams: number;
|
||||||
draftSlots: any[];
|
draftSlots: DraftSlot[];
|
||||||
db?: ReturnType<typeof database>;
|
db?: ReturnType<typeof database>;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const { seasonId, totalTeams, draftSlots, db: providedDb } = params;
|
const { seasonId, totalTeams, draftSlots, db: providedDb } = params;
|
||||||
|
|
@ -367,20 +370,26 @@ export function getTeamForPick(
|
||||||
* @param params.db - Database instance (optional, will use database() if not provided)
|
* @param params.db - Database instance (optional, will use database() if not provided)
|
||||||
* @returns Result object with success status and pick data
|
* @returns Result object with success status and pick data
|
||||||
*/
|
*/
|
||||||
|
type AutodraftSettings = InferSelectModel<typeof schema.autodraftSettings>;
|
||||||
|
|
||||||
export async function executeAutoPick(params: {
|
export async function executeAutoPick(params: {
|
||||||
seasonId: string;
|
seasonId: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
pickNumber: number;
|
pickNumber: number;
|
||||||
triggeredBy: "commissioner" | "timer";
|
triggeredBy: "commissioner" | "timer";
|
||||||
commissionerUserId?: string;
|
commissionerUserId?: string;
|
||||||
autodraftSettings?: any;
|
autodraftSettings?: AutodraftSettings | null;
|
||||||
db?: ReturnType<typeof database>;
|
db?: ReturnType<typeof database>;
|
||||||
chainEnabled?: boolean; // Set to false when called from within the autodraft chain to prevent recursion
|
chainEnabled?: boolean; // Set to false when called from within the autodraft chain to prevent recursion
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
success: boolean;
|
success: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
pick?: any;
|
pick?: InferSelectModel<typeof schema.draftPicks>;
|
||||||
participant?: any;
|
participant?: InferSelectModel<typeof schema.participants> & {
|
||||||
|
sportsSeason: InferSelectModel<typeof schema.sportsSeasons> & {
|
||||||
|
sport: InferSelectModel<typeof schema.sports>;
|
||||||
|
};
|
||||||
|
};
|
||||||
nextPickNumber?: number;
|
nextPickNumber?: number;
|
||||||
isDraftComplete?: boolean;
|
isDraftComplete?: boolean;
|
||||||
}> {
|
}> {
|
||||||
|
|
|
||||||
|
|
@ -220,8 +220,35 @@ describe("draft.force-manual-pick action", () => {
|
||||||
expect(response.status).toBe(404);
|
expect(response.status).toBe(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns 400 when the draft is not active", async () => {
|
||||||
|
mockDb.query.seasons.findFirst.mockResolvedValue({ ...mockSeason, status: "pre_draft" });
|
||||||
|
|
||||||
|
const response = await action({ request: defaultPickRequest(), params: {}, context: {} });
|
||||||
|
|
||||||
|
expect(response.status).toBe(400);
|
||||||
|
const data = await response.json();
|
||||||
|
expect(data.error).toMatch(/not currently active/i);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 400 when a pick already exists at the requested slot", async () => {
|
||||||
|
mockDb.query.draftPicks.findFirst.mockResolvedValueOnce({
|
||||||
|
id: "existing-pick",
|
||||||
|
participantId: "other-participant",
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await action({ request: defaultPickRequest(), params: {}, context: {} });
|
||||||
|
|
||||||
|
expect(response.status).toBe(400);
|
||||||
|
const data = await response.json();
|
||||||
|
expect(data.error).toMatch(/already exists at this slot/i);
|
||||||
|
});
|
||||||
|
|
||||||
it("returns 400 when the participant is already drafted", async () => {
|
it("returns 400 when the participant is already drafted", async () => {
|
||||||
mockDb.query.draftPicks.findFirst.mockResolvedValue({
|
// First call: slot check (no pick at this slot number)
|
||||||
|
// Second call: participant check (participant already drafted elsewhere)
|
||||||
|
mockDb.query.draftPicks.findFirst
|
||||||
|
.mockResolvedValueOnce(null)
|
||||||
|
.mockResolvedValueOnce({
|
||||||
id: "existing-pick",
|
id: "existing-pick",
|
||||||
participantId: PARTICIPANT_ID,
|
participantId: PARTICIPANT_ID,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import * as schema from "~/database/schema";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { getSocketIO } from "~/server/socket";
|
import { getSocketIO } from "~/server/socket";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import { database } from "~/database/context";
|
||||||
import * as schema from "~/database/schema";
|
import * as schema from "~/database/schema";
|
||||||
import { getSocketIO } from "../../../server/socket";
|
import { getSocketIO } from "../../../server/socket";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import * as schema from "~/database/schema";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { executeAutoPick } from "~/models/draft-utils";
|
import { executeAutoPick } from "~/models/draft-utils";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@ import { getDraftPicksWithSports, getTeamDraftPicksWithSports } from "~/models/d
|
||||||
import { getParticipantsForSeasonWithSports } from "~/models/participant";
|
import { getParticipantsForSeasonWithSports } from "~/models/participant";
|
||||||
import { getSeasonSportsSimple } from "~/models/season-sport";
|
import { getSeasonSportsSimple } from "~/models/season-sport";
|
||||||
import { getSocketIO } from "../../../server/socket";
|
import { getSocketIO } from "../../../server/socket";
|
||||||
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
|
||||||
export async function action(args: any) {
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
@ -50,6 +50,22 @@ export async function action(args: any) {
|
||||||
return Response.json({ error: "Only commissioners can force a manual pick" }, { status: 403 });
|
return Response.json({ error: "Only commissioners can force a manual pick" }, { status: 403 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (season.status !== "draft") {
|
||||||
|
return Response.json({ error: "Draft is not currently active" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a pick already exists at this pick number slot (prevents duplicate picks at same slot)
|
||||||
|
const existingPickAtSlot = await db.query.draftPicks.findFirst({
|
||||||
|
where: and(
|
||||||
|
eq(schema.draftPicks.seasonId, seasonId),
|
||||||
|
eq(schema.draftPicks.pickNumber, pickNumber)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingPickAtSlot) {
|
||||||
|
return Response.json({ error: "A pick already exists at this slot" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
// Check if participant is already drafted
|
// Check if participant is already drafted
|
||||||
const existingPick = await db.query.draftPicks.findFirst({
|
const existingPick = await db.query.draftPicks.findFirst({
|
||||||
where: and(
|
where: and(
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ import { getParticipantsForSeasonWithSports } from "~/models/participant";
|
||||||
import { getSeasonSportsSimple } from "~/models/season-sport";
|
import { getSeasonSportsSimple } from "~/models/season-sport";
|
||||||
import { getSocketIO } from "../../../server/socket";
|
import { getSocketIO } from "../../../server/socket";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
@ -56,12 +56,11 @@ export async function action(args: any) {
|
||||||
|
|
||||||
const totalTeams = draftSlots.length;
|
const totalTeams = draftSlots.length;
|
||||||
const currentRound = Math.ceil(currentPickNumber / totalTeams);
|
const currentRound = Math.ceil(currentPickNumber / totalTeams);
|
||||||
const isSnakeDraft = true; // Assuming snake draft
|
|
||||||
const isEvenRound = currentRound % 2 === 0;
|
const isEvenRound = currentRound % 2 === 0;
|
||||||
|
|
||||||
// Calculate which team should pick
|
// Calculate which team should pick (snake draft)
|
||||||
let pickInRound = ((currentPickNumber - 1) % totalTeams) + 1;
|
let pickInRound = ((currentPickNumber - 1) % totalTeams) + 1;
|
||||||
if (isSnakeDraft && isEvenRound) {
|
if (isEvenRound) {
|
||||||
pickInRound = totalTeams - pickInRound + 1;
|
pickInRound = totalTeams - pickInRound + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import { database } from "~/database/context";
|
||||||
import * as schema from "~/database/schema";
|
import * as schema from "~/database/schema";
|
||||||
import { getSocketIO } from "../../../server/socket";
|
import { getSocketIO } from "../../../server/socket";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ import { getParticipantsForSeasonWithSports } from "~/models/participant";
|
||||||
import { getSeasonSportsSimple } from "~/models/season-sport";
|
import { getSeasonSportsSimple } from "~/models/season-sport";
|
||||||
import { getSocketIO } from "../../../server/socket";
|
import { getSocketIO } from "../../../server/socket";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import { database } from "~/database/context";
|
||||||
import * as schema from "~/database/schema";
|
import * as schema from "~/database/schema";
|
||||||
import { getSocketIO } from "../../../server/socket";
|
import { getSocketIO } from "../../../server/socket";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import * as schema from "~/database/schema";
|
||||||
import { eq, and, gte } from "drizzle-orm";
|
import { eq, and, gte } from "drizzle-orm";
|
||||||
import { getSocketIO } from "../../../server/socket";
|
import { getSocketIO } from "../../../server/socket";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import * as schema from "~/database/schema";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { getSocketIO } from "../../../server/socket";
|
import { getSocketIO } from "../../../server/socket";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import * as schema from "~/database/schema";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { addToQueue, getTeamQueue, isParticipantInQueue } from "~/models/draft-queue";
|
import { addToQueue, getTeamQueue, isParticipantInQueue } from "~/models/draft-queue";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ import { database } from "~/database/context";
|
||||||
import * as schema from "~/database/schema";
|
import * as schema from "~/database/schema";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { clearTeamQueue } from "~/models/draft-queue";
|
import { clearTeamQueue } from "~/models/draft-queue";
|
||||||
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
|
||||||
export async function action(args: any) {
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import * as schema from "~/database/schema";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { removeFromQueue, getTeamQueue } from "~/models/draft-queue";
|
import { removeFromQueue, getTeamQueue } from "~/models/draft-queue";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import * as schema from "~/database/schema";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { reorderQueueWithSeason, getTeamQueue } from "~/models/draft-queue";
|
import { reorderQueueWithSeason, getTeamQueue } from "~/models/draft-queue";
|
||||||
|
|
||||||
export async function action(args: any) {
|
import type { ActionFunctionArgs } from "react-router";
|
||||||
|
export async function action(args: ActionFunctionArgs) {
|
||||||
const { request } = args;
|
const { request } = args;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@ import { DraftGrid } from "~/components/DraftGrid";
|
||||||
import { useDraftSocket } from "~/hooks/useDraftSocket";
|
import { useDraftSocket } from "~/hooks/useDraftSocket";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { buildOwnerMap } from "~/lib/owner-map";
|
import { buildOwnerMap } from "~/lib/owner-map";
|
||||||
|
import type { Route } from "./+types/$leagueId.draft-board.$seasonId";
|
||||||
|
|
||||||
export async function loader(args: any) {
|
export async function loader(args: Route.LoaderArgs) {
|
||||||
const { params } = args;
|
const { params } = args;
|
||||||
const { leagueId, seasonId } = params;
|
const { leagueId, seasonId } = params;
|
||||||
|
|
||||||
|
|
@ -38,8 +39,7 @@ export async function loader(args: any) {
|
||||||
// Check access: public boards are accessible to everyone without auth
|
// Check access: public boards are accessible to everyone without auth
|
||||||
if (!season.league.isPublicDraftBoard) {
|
if (!season.league.isPublicDraftBoard) {
|
||||||
// Not public - check if the user is a league member or commissioner
|
// Not public - check if the user is a league member or commissioner
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new Response("This draft board is not public", { status: 403 });
|
throw new Response("This draft board is not public", { status: 403 });
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@ import { useDraftNotifications } from "~/hooks/useDraftNotifications";
|
||||||
import { NotificationSettings } from "~/components/NotificationSettings";
|
import { NotificationSettings } from "~/components/NotificationSettings";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { formatClockTime, getTimerColorClass } from "~/lib/draft-timer";
|
import { formatClockTime, getTimerColorClass } from "~/lib/draft-timer";
|
||||||
|
import type { Route } from "./+types/$leagueId.draft.$seasonId";
|
||||||
|
|
||||||
export async function loader(args: any) {
|
export async function loader(args: Route.LoaderArgs) {
|
||||||
const { params } = args;
|
const { params } = args;
|
||||||
const { seasonId, leagueId } = params;
|
const { seasonId, leagueId } = params;
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
|
|
||||||
if (!seasonId) {
|
if (!seasonId) {
|
||||||
throw new Response("Season ID is required", { status: 400 });
|
throw new Response("Season ID is required", { status: 400 });
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,9 @@ import {
|
||||||
findDraftSlotsBySeasonId,
|
findDraftSlotsBySeasonId,
|
||||||
} from "~/models";
|
} from "~/models";
|
||||||
import { findCurrentSeasonWithSports } from "~/models/season";
|
import { findCurrentSeasonWithSports } from "~/models/season";
|
||||||
|
import type { Route } from "./+types/$leagueId";
|
||||||
|
|
||||||
export async function loader(args: any) {
|
export async function loader(args: Route.LoaderArgs) {
|
||||||
const { userId } = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const { params } = args;
|
const { params } = args;
|
||||||
const { leagueId } = params;
|
const { leagueId } = params;
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ import { StandingsTable } from "~/components/standings/StandingsTable";
|
||||||
import { getSevenDayStandingsChange, getSeasonPointProgression } from "~/models/standings";
|
import { getSevenDayStandingsChange, getSeasonPointProgression } from "~/models/standings";
|
||||||
import { isSeasonComplete, getSeasonCompletionPercentage } from "~/lib/season-helpers.server";
|
import { isSeasonComplete, getSeasonCompletionPercentage } from "~/lib/season-helpers.server";
|
||||||
import { PointProgressionChart } from "~/components/standings/PointProgressionChart";
|
import { PointProgressionChart } from "~/components/standings/PointProgressionChart";
|
||||||
|
import type { Route } from "./+types/$leagueId.standings.$seasonId";
|
||||||
|
|
||||||
export async function loader(args: any) {
|
export async function loader(args: Route.LoaderArgs) {
|
||||||
try {
|
try {
|
||||||
const auth = await getAuth(args);
|
const { userId } = await getAuth(args);
|
||||||
const userId = (auth as any).userId as string | null;
|
|
||||||
const { params } = args;
|
const { params } = args;
|
||||||
const { leagueId, seasonId } = params;
|
const { leagueId, seasonId } = params;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ interface ServerToClientEvents {
|
||||||
"team-connected": (data: { teamId: string }) => void;
|
"team-connected": (data: { teamId: string }) => void;
|
||||||
"team-disconnected": (data: { teamId: string }) => void;
|
"team-disconnected": (data: { teamId: string }) => void;
|
||||||
"connected-teams-list": (data: { teamIds: string[] }) => void;
|
"connected-teams-list": (data: { teamIds: string[] }) => void;
|
||||||
"draft-paused": () => void;
|
"draft-paused": (data: { seasonId: string; paused: boolean }) => void;
|
||||||
"draft-resumed": () => void;
|
"draft-resumed": (data: { seasonId: string; paused: boolean }) => void;
|
||||||
"participant-removed-from-queues": (data: { participantId: string }) => void;
|
"participant-removed-from-queues": (data: { participantId: string }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue