diff --git a/app/components/DraftGrid.tsx b/app/components/DraftGrid.tsx new file mode 100644 index 0000000..d570deb --- /dev/null +++ b/app/components/DraftGrid.tsx @@ -0,0 +1,157 @@ +import { Card } from "~/components/ui/card"; +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuTrigger, +} from "~/components/ui/context-menu"; + +interface DraftGridProps { + draftSlots: Array<{ + id: string; + draftOrder: number; + team: { + id: string; + name: string; + }; + }>; + draftGrid: any[][]; + currentPick: number; + teamTimers?: Record; + formatTime?: (seconds: number | undefined) => string; + title?: string; + isCommissioner?: boolean; + onForceAutopick?: (pickNumber: number, teamId: string) => void; + onForceManualPick?: (pickNumber: number, teamId: string) => void; +} + +export function DraftGrid({ + draftSlots, + draftGrid, + currentPick, + teamTimers, + formatTime, + title, + isCommissioner = false, + onForceAutopick, + onForceManualPick, +}: DraftGridProps) { + const totalTeams = draftSlots.length; + + return ( + + {title &&

{title}

} +
+ {/* Team Headers */} +
+ {draftSlots.map((slot) => { + const teamTime = teamTimers?.[slot.team.id]; + return ( +
+
+ {slot.team.name} +
+ {teamTimers && formatTime && ( +
60 + ? "text-green-600" + : teamTime > 30 + ? "text-yellow-600" + : "text-red-600" + }`} + > + {formatTime(teamTime)} +
+ )} +
+ ); + })} +
+ + {/* Draft Grid Rows */} +
+ {draftGrid.map((roundPicks, roundIndex) => { + const round = roundIndex + 1; + const isEvenRound = round % 2 === 0; + const displayPicks = isEvenRound + ? [...roundPicks].reverse() + : roundPicks; + + return ( +
+ {displayPicks.map((cell, index) => { + const actualIndex = isEvenRound + ? roundPicks.length - 1 - index + : index; + const slot = draftSlots[actualIndex]; + const pickNumber = roundIndex * totalTeams + actualIndex + 1; + const isCurrent = pickNumber === currentPick; + const isPicked = !!cell; + + const cellContent = ( +
+
+ {round}.{String(actualIndex + 1).padStart(2, "0")} +
+ {isPicked ? ( +
+
+ {cell.participant.name} +
+
+ {cell.sport.name} +
+
+ ) : isCurrent ? ( +
+ On Clock +
+ ) : null} +
+ ); + + // Wrap with context menu if commissioner and current unpicked cell + if (isCommissioner && !isPicked && isCurrent && onForceAutopick && onForceManualPick) { + return ( + + + {cellContent} + + + onForceAutopick(pickNumber, slot.team.id)} + > + Force Auto Pick + + onForceManualPick(pickNumber, slot.team.id)} + > + Force Manual Pick + + + + ); + } + + return cellContent; + })} +
+ ); + })} +
+
+
+ ); +} diff --git a/app/routes.ts b/app/routes.ts index 5fb8e41..cce232c 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -10,6 +10,10 @@ export default [ "leagues/:leagueId/draft/:seasonId", "routes/leagues/$leagueId.draft.$seasonId.tsx" ), + route( + "leagues/:leagueId/draft-board/:seasonId", + "routes/leagues/$leagueId.draft-board.$seasonId.tsx" + ), route("teams/:teamId/settings", "routes/teams/$teamId.settings.tsx"), route("api/webhooks/clerk", "routes/api/webhooks/clerk.ts"), route("api/queue/add", "routes/api/queue.add.ts"), diff --git a/app/routes/api/draft.pause.ts b/app/routes/api/draft.pause.ts index 63858a8..e816c5f 100644 --- a/app/routes/api/draft.pause.ts +++ b/app/routes/api/draft.pause.ts @@ -1,4 +1,4 @@ -import { getAuth } from "@clerk/react-router/ssr.server"; +import { getAuth } from "@clerk/react-router/server"; import { eq, and } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; @@ -50,10 +50,7 @@ export async function action(args: any) { // Check if draft is active if (season.status !== "draft") { - return Response.json( - { error: "Draft is not active" }, - { status: 400 } - ); + return Response.json({ error: "Draft is not active" }, { status: 400 }); } // Pause the draft diff --git a/app/routes/api/draft.resume.ts b/app/routes/api/draft.resume.ts index bb09462..57c31fe 100644 --- a/app/routes/api/draft.resume.ts +++ b/app/routes/api/draft.resume.ts @@ -1,4 +1,4 @@ -import { getAuth } from "@clerk/react-router/ssr.server"; +import { getAuth } from "@clerk/react-router/server"; import { eq, and } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; @@ -50,10 +50,7 @@ export async function action(args: any) { // Check if draft is active if (season.status !== "draft") { - return Response.json( - { error: "Draft is not active" }, - { status: 400 } - ); + return Response.json({ error: "Draft is not active" }, { status: 400 }); } // Resume the draft diff --git a/app/routes/leagues/$leagueId.draft-board.$seasonId.tsx b/app/routes/leagues/$leagueId.draft-board.$seasonId.tsx new file mode 100644 index 0000000..a254e44 --- /dev/null +++ b/app/routes/leagues/$leagueId.draft-board.$seasonId.tsx @@ -0,0 +1,186 @@ +import { useLoaderData } from "react-router"; +import { eq, asc, and } from "drizzle-orm"; +import { getAuth } from "@clerk/react-router/server"; +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; +import { DraftGrid } from "~/components/DraftGrid"; +import { useDraftSocket } from "~/hooks/useDraftSocket"; +import { useState, useEffect } from "react"; + +export async function loader(args: any) { + const { params } = args; + const { seasonId } = params; + const auth = await getAuth(args); + const userId = (auth as any).userId as string | null; + + if (!seasonId) { + throw new Response("Season ID is required", { status: 400 }); + } + + const db = database(); + + // Get season details + const season = await db.query.seasons.findFirst({ + where: eq(schema.seasons.id, seasonId), + with: { + league: true, + }, + }); + + if (!season) { + throw new Response("Season not found", { status: 404 }); + } + + // Check access: allow if public OR user is a league member/commissioner + let hasAccess = season.league.isPublicDraftBoard; + + if (!hasAccess && userId) { + // Check if user is a commissioner + const isCommissioner = await db.query.commissioners.findFirst({ + where: and( + eq(schema.commissioners.leagueId, season.leagueId), + eq(schema.commissioners.userId, userId) + ), + }); + + // Check if user has a team in this season + const hasTeam = await db.query.teams.findFirst({ + where: and( + eq(schema.teams.seasonId, seasonId), + eq(schema.teams.ownerId, userId) + ), + }); + + hasAccess = !!(isCommissioner || hasTeam); + } + + if (!hasAccess) { + throw new Response("This draft board is not public", { status: 403 }); + } + + // Get draft slots (draft order) + const draftSlots = await db + .select({ + id: schema.draftSlots.id, + draftOrder: schema.draftSlots.draftOrder, + team: schema.teams, + }) + .from(schema.draftSlots) + .innerJoin(schema.teams, eq(schema.draftSlots.teamId, schema.teams.id)) + .where(eq(schema.draftSlots.seasonId, seasonId)) + .orderBy(asc(schema.draftSlots.draftOrder)); + + // Get all draft picks with participant and sport info + const draftPicks = await db + .select({ + id: schema.draftPicks.id, + pickNumber: schema.draftPicks.pickNumber, + round: schema.draftPicks.round, + pickInRound: schema.draftPicks.pickInRound, + team: schema.teams, + participant: schema.participants, + sport: schema.sports, + }) + .from(schema.draftPicks) + .innerJoin(schema.teams, eq(schema.draftPicks.teamId, schema.teams.id)) + .innerJoin( + schema.participants, + eq(schema.draftPicks.participantId, schema.participants.id) + ) + .innerJoin( + schema.sportsSeasons, + eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id) + ) + .innerJoin(schema.sports, eq(schema.sportsSeasons.sportId, schema.sports.id)) + .where(eq(schema.draftPicks.seasonId, seasonId)) + .orderBy(asc(schema.draftPicks.pickNumber)); + + return { + season, + draftSlots, + draftPicks, + }; +} + +export default function DraftBoard() { + const { season, draftSlots, draftPicks: initialPicks } = useLoaderData(); + const { isConnected, on, off } = useDraftSocket(season.id); + const [picks, setPicks] = useState(initialPicks); + const [currentPick, setCurrentPick] = useState(season.currentPickNumber || 1); + + // Listen for new picks (only if draft is still active) + useEffect(() => { + if (season.status !== "draft") return; + + const handlePickMade = (data: any) => { + setPicks((prev: any) => [...prev, data.pick]); + setCurrentPick(data.nextPickNumber); + }; + + on("pick-made", handlePickMade); + + return () => { + off("pick-made", handlePickMade); + }; + }, [on, off, season.status]); + + // Generate draft grid + const totalTeams = draftSlots.length; + const totalRounds = season.draftRounds || 1; + const draftGrid: any[][] = []; + + for (let round = 0; round < totalRounds; round++) { + const roundPicks: any[] = []; + for (let teamIndex = 0; teamIndex < totalTeams; teamIndex++) { + const pickNumber = round * totalTeams + teamIndex + 1; + const pick = picks.find((p: any) => p.pickNumber === pickNumber); + roundPicks.push(pick || null); + } + draftGrid.push(roundPicks); + } + + return ( +
+ {/* Header */} +
+
+
+
+

+ {season.league.name} - {season.year} Draft Board +

+
+ Round: {Math.ceil(currentPick / totalTeams)} + Pick: {currentPick} + + {season.status.replace("_", " ")} + +
+
+ {season.status === "draft" && ( +
+
+ + {isConnected ? "Live" : "Disconnected"} + +
+ )} +
+
+
+ + {/* Draft Grid */} +
+ +
+
+ ); +} diff --git a/app/routes/leagues/$leagueId.draft.$seasonId.tsx b/app/routes/leagues/$leagueId.draft.$seasonId.tsx index af86748..53ad208 100644 --- a/app/routes/leagues/$leagueId.draft.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft.$seasonId.tsx @@ -184,11 +184,22 @@ export default function DraftRoom() { const [isPaused, setIsPaused] = useState(season.draftPaused || false); const [isDraftComplete, setIsDraftComplete] = useState(season.status === "active"); const [teamTimers, setTeamTimers] = useState>(() => { - // Initialize with timers from loader data + // Initialize with timers from loader data, or use initial time if draft hasn't started const timersMap: Record = {}; - timers.forEach((timer: any) => { - timersMap[timer.teamId] = timer.timeRemaining; - }); + const initialTime = season.draftInitialTime || 120; + + if (timers.length > 0) { + // Draft has started, use actual timers + timers.forEach((timer: any) => { + timersMap[timer.teamId] = timer.timeRemaining; + }); + } else { + // Draft hasn't started, show initial time for all teams + draftSlots.forEach((slot) => { + timersMap[slot.team.id] = initialTime; + }); + } + return timersMap; }); const [forcePickDialogOpen, setForcePickDialogOpen] = useState(false); @@ -518,6 +529,17 @@ export default function DraftRoom() { {isDraftComplete && (
🎉 Draft Complete! The season is now active. + {season.league.isPublicDraftBoard && ( + <> + {" "} + + View Draft Board + + + )}
)} diff --git a/app/routes/leagues/$leagueId.settings.tsx b/app/routes/leagues/$leagueId.settings.tsx index 0c30c97..0950094 100644 --- a/app/routes/leagues/$leagueId.settings.tsx +++ b/app/routes/leagues/$leagueId.settings.tsx @@ -212,8 +212,34 @@ export async function action(args: Route.ActionArgs) { const teamCount = formData.get("teamCount"); const draftDateTime = formData.get("draftDateTime"); const draftRounds = formData.get("draftRounds"); - const draftInitialTime = formData.get("draftInitialTime"); - const draftIncrementTime = formData.get("draftIncrementTime"); + const draftSpeed = formData.get("draftSpeed"); + const isPublicDraftBoard = formData.get("isPublicDraftBoard") === "on"; + + // Map draft speed to time values + let draftInitialTime: number; + let draftIncrementTime: number; + + switch (draftSpeed) { + case "fast": + draftInitialTime = 60; + draftIncrementTime = 10; + break; + case "standard": + draftInitialTime = 120; + draftIncrementTime = 15; + break; + case "slow": + draftInitialTime = 28800; // 8 hours + draftIncrementTime = 3600; // 1 hour + break; + case "very-slow": + draftInitialTime = 43200; // 12 hours + draftIncrementTime = 3600; // 1 hour + break; + default: + draftInitialTime = 120; + draftIncrementTime = 15; + } // Validation if (typeof name !== "string" || !name.trim()) { @@ -227,6 +253,7 @@ export async function action(args: Route.ActionArgs) { try { await updateLeague(leagueId, { name: name.trim(), + isPublicDraftBoard, }); // Update season settings @@ -252,27 +279,9 @@ export async function action(args: Route.ActionArgs) { seasonUpdates.draftDateTime = draftDateTime ? new Date(draftDateTime) : null; } - // Handle draft initial time - if (typeof draftInitialTime === "string") { - const initialTime = parseInt(draftInitialTime, 10); - if (!isNaN(initialTime)) { - if (initialTime < 30 || initialTime > 86400) { - return { error: "Initial draft time must be between 30 seconds and 24 hours (86400 seconds)" }; - } - seasonUpdates.draftInitialTime = initialTime; - } - } - - // Handle draft increment time - if (typeof draftIncrementTime === "string") { - const incrementTime = parseInt(draftIncrementTime, 10); - if (!isNaN(incrementTime)) { - if (incrementTime < 0 || incrementTime > 3600) { - return { error: "Draft increment time must be between 0 and 1 hour (3600 seconds)" }; - } - seasonUpdates.draftIncrementTime = incrementTime; - } - } + // Set draft times from speed preset + seasonUpdates.draftInitialTime = draftInitialTime; + seasonUpdates.draftIncrementTime = draftIncrementTime; // Update season if there are changes if (Object.keys(seasonUpdates).length > 0) { @@ -438,6 +447,19 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone />
+
+ + +
+
Draft Speed +

- Time each team starts with (30 seconds to 24 hours) -

-
- -
- - -

- Time added after each pick (0 to 1 hour) + Initial time + increment per pick

+ +
diff --git a/app/routes/leagues/$leagueId.tsx b/app/routes/leagues/$leagueId.tsx index 4ee5e1d..624b2e6 100644 --- a/app/routes/leagues/$leagueId.tsx +++ b/app/routes/leagues/$leagueId.tsx @@ -364,6 +364,17 @@ export default function LeagueHome({ loaderData }: Route.ComponentProps) {

Sports Selected

{sportsCount}

+ {(season.status === "active" || season.status === "completed") && ( +
+

Draft Board

+ + View Draft Board + +
+ )}

Flex Spots

diff --git a/app/routes/leagues/new.tsx b/app/routes/leagues/new.tsx index 4bfdaa3..3816e35 100644 --- a/app/routes/leagues/new.tsx +++ b/app/routes/leagues/new.tsx @@ -76,8 +76,33 @@ export async function action(args: Route.ActionArgs) { const templateId = formData.get("templateId"); const draftRounds = formData.get("draftRounds"); const draftDateTime = formData.get("draftDateTime"); - const draftInitialTime = formData.get("draftInitialTime"); - const draftIncrementTime = formData.get("draftIncrementTime"); + const draftSpeed = formData.get("draftSpeed"); + + // Map draft speed to time values + let draftInitialTimeNum: number; + let draftIncrementTimeNum: number; + + switch (draftSpeed) { + case "fast": + draftInitialTimeNum = 60; + draftIncrementTimeNum = 10; + break; + case "standard": + draftInitialTimeNum = 120; + draftIncrementTimeNum = 15; + break; + case "slow": + draftInitialTimeNum = 28800; // 8 hours + draftIncrementTimeNum = 3600; // 1 hour + break; + case "very-slow": + draftInitialTimeNum = 43200; // 12 hours + draftIncrementTimeNum = 3600; // 1 hour + break; + default: + draftInitialTimeNum = 120; + draftIncrementTimeNum = 15; + } // Validation if (typeof name !== "string" || !name.trim()) { @@ -98,16 +123,6 @@ export async function action(args: Route.ActionArgs) { return { error: "Draft rounds must be between 1 and 50" }; } - const draftInitialTimeNum = typeof draftInitialTime === "string" ? parseInt(draftInitialTime, 10) : 120; - if (isNaN(draftInitialTimeNum) || draftInitialTimeNum < 30 || draftInitialTimeNum > 86400) { - return { error: "Initial draft time must be between 30 seconds and 24 hours" }; - } - - const draftIncrementTimeNum = typeof draftIncrementTime === "string" ? parseInt(draftIncrementTime, 10) : 30; - if (isNaN(draftIncrementTimeNum) || draftIncrementTimeNum < 0 || draftIncrementTimeNum > 3600) { - return { error: "Draft increment time must be between 0 and 1 hour" }; - } - try { // Create league const league = await createLeague({ @@ -341,34 +356,21 @@ export default function NewLeague({ loaderData, actionData }: Route.ComponentPro

- - Draft Speed +

- Time each team starts with (30 seconds to 24 hours). Default: 120 seconds (2 minutes) -

-
- -
- - -

- Time added after each pick (0 to 1 hour). Default: 30 seconds + Initial time + increment per pick

diff --git a/database/schema.ts b/database/schema.ts index 307079c..bef57bb 100644 --- a/database/schema.ts +++ b/database/schema.ts @@ -50,6 +50,7 @@ export const leagues = pgTable("leagues", { name: varchar("name", { length: 255 }).notNull(), createdBy: varchar("created_by", { length: 255 }).notNull(), // Clerk user ID currentSeasonId: uuid("current_season_id"), // References the active season + isPublicDraftBoard: boolean("is_public_draft_board").notNull().default(false), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().notNull(), }); diff --git a/drizzle/0018_numerous_stardust.sql b/drizzle/0018_numerous_stardust.sql new file mode 100644 index 0000000..7059518 --- /dev/null +++ b/drizzle/0018_numerous_stardust.sql @@ -0,0 +1 @@ +ALTER TABLE "leagues" ADD COLUMN "is_public_draft_board" boolean DEFAULT false NOT NULL; \ No newline at end of file diff --git a/drizzle/meta/0018_snapshot.json b/drizzle/meta/0018_snapshot.json new file mode 100644 index 0000000..533e286 --- /dev/null +++ b/drizzle/meta/0018_snapshot.json @@ -0,0 +1,1401 @@ +{ + "id": "35cf6263-ca97-41f5-88ba-2f62468075b1", + "prevId": "c4b604e2-a0eb-4e60-93b5-8c3c0c76cdd6", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.commissioners": { + "name": "commissioners", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "commissioners_league_id_leagues_id_fk": { + "name": "commissioners_league_id_leagues_id_fk", + "tableFrom": "commissioners", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_picks": { + "name": "draft_picks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pick_number": { + "name": "pick_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "round": { + "name": "round", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pick_in_round": { + "name": "pick_in_round", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "picked_by_user_id": { + "name": "picked_by_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "picked_by_type": { + "name": "picked_by_type", + "type": "picked_by_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "time_used": { + "name": "time_used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_picks_season_id_seasons_id_fk": { + "name": "draft_picks_season_id_seasons_id_fk", + "tableFrom": "draft_picks", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_picks_team_id_teams_id_fk": { + "name": "draft_picks_team_id_teams_id_fk", + "tableFrom": "draft_picks", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_picks_participant_id_participants_id_fk": { + "name": "draft_picks_participant_id_participants_id_fk", + "tableFrom": "draft_picks", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_queue": { + "name": "draft_queue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "queue_position": { + "name": "queue_position", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_queue_season_id_seasons_id_fk": { + "name": "draft_queue_season_id_seasons_id_fk", + "tableFrom": "draft_queue", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_queue_team_id_teams_id_fk": { + "name": "draft_queue_team_id_teams_id_fk", + "tableFrom": "draft_queue", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_queue_participant_id_participants_id_fk": { + "name": "draft_queue_participant_id_participants_id_fk", + "tableFrom": "draft_queue", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_slots": { + "name": "draft_slots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "draft_order": { + "name": "draft_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_slots_season_id_seasons_id_fk": { + "name": "draft_slots_season_id_seasons_id_fk", + "tableFrom": "draft_slots", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_slots_team_id_teams_id_fk": { + "name": "draft_slots_team_id_teams_id_fk", + "tableFrom": "draft_slots", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_timers": { + "name": "draft_timers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "time_remaining": { + "name": "time_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_timers_season_id_seasons_id_fk": { + "name": "draft_timers_season_id_seasons_id_fk", + "tableFrom": "draft_timers", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_timers_team_id_teams_id_fk": { + "name": "draft_timers_team_id_teams_id_fk", + "tableFrom": "draft_timers", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.leagues": { + "name": "leagues", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "current_season_id": { + "name": "current_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_public_draft_board": { + "name": "is_public_draft_board", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_results": { + "name": "participant_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "final_position": { + "name": "final_position", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "points_awarded": { + "name": "points_awarded", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "qualifying_points": { + "name": "qualifying_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "participant_results_participant_id_participants_id_fk": { + "name": "participant_results_participant_id_participants_id_fk", + "tableFrom": "participant_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_results_sports_season_id_sports_seasons_id_fk": { + "name": "participant_results_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_results", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participants": { + "name": "participants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "short_name": { + "name": "short_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "expected_value": { + "name": "expected_value", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "participants_sports_season_id_sports_seasons_id_fk": { + "name": "participants_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participants", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_sports": { + "name": "season_sports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "season_sports_season_id_seasons_id_fk": { + "name": "season_sports_season_id_seasons_id_fk", + "tableFrom": "season_sports", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_sports_sports_season_id_sports_seasons_id_fk": { + "name": "season_sports_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_sports", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_template_sports": { + "name": "season_template_sports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "season_template_sports_template_id_season_templates_id_fk": { + "name": "season_template_sports_template_id_season_templates_id_fk", + "tableFrom": "season_template_sports", + "tableTo": "season_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_template_sports_sports_season_id_sports_seasons_id_fk": { + "name": "season_template_sports_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_template_sports", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_templates": { + "name": "season_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.seasons": { + "name": "seasons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "season_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pre_draft'" + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "draft_rounds": { + "name": "draft_rounds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20 + }, + "flex_spots": { + "name": "flex_spots", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "draft_date_time": { + "name": "draft_date_time", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_initial_time": { + "name": "draft_initial_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 120 + }, + "draft_increment_time": { + "name": "draft_increment_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 30 + }, + "current_pick_number": { + "name": "current_pick_number", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "draft_started_at": { + "name": "draft_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_paused": { + "name": "draft_paused", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "invite_code": { + "name": "invite_code", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "seasons_league_id_leagues_id_fk": { + "name": "seasons_league_id_leagues_id_fk", + "tableFrom": "seasons", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "seasons_template_id_season_templates_id_fk": { + "name": "seasons_template_id_season_templates_id_fk", + "tableFrom": "seasons", + "tableTo": "season_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "seasons_invite_code_unique": { + "name": "seasons_invite_code_unique", + "nullsNotDistinct": false, + "columns": [ + "invite_code" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sports": { + "name": "sports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "sport_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon_url": { + "name": "icon_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sports_slug_unique": { + "name": "sports_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sports_seasons": { + "name": "sports_seasons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sport_id": { + "name": "sport_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "start_date": { + "name": "start_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "sports_season_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'upcoming'" + }, + "scoring_type": { + "name": "scoring_type", + "type": "scoring_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "sports_seasons_sport_id_sports_id_fk": { + "name": "sports_seasons_sport_id_sports_id_fk", + "tableFrom": "sports_seasons", + "tableTo": "sports", + "columnsFrom": [ + "sport_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams": { + "name": "teams", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "logo_url": { + "name": "logo_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "teams_season_id_seasons_id_fk": { + "name": "teams_season_id_seasons_id_fk", + "tableFrom": "teams", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "clerk_id": { + "name": "clerk_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_clerk_id_unique": { + "name": "users_clerk_id_unique", + "nullsNotDistinct": false, + "columns": [ + "clerk_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.picked_by_type": { + "name": "picked_by_type", + "schema": "public", + "values": [ + "owner", + "commissioner", + "auto" + ] + }, + "public.scoring_type": { + "name": "scoring_type", + "schema": "public", + "values": [ + "playoffs", + "regular_season", + "majors" + ] + }, + "public.season_status": { + "name": "season_status", + "schema": "public", + "values": [ + "pre_draft", + "draft", + "active", + "completed" + ] + }, + "public.sport_type": { + "name": "sport_type", + "schema": "public", + "values": [ + "team", + "individual" + ] + }, + "public.sports_season_status": { + "name": "sports_season_status", + "schema": "public", + "values": [ + "upcoming", + "active", + "completed" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index b506590..bf62387 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -127,6 +127,13 @@ "when": 1760599350154, "tag": "0017_calm_zarda", "breakpoints": true + }, + { + "idx": 18, + "version": "7", + "when": 1760996053371, + "tag": "0018_numerous_stardust", + "breakpoints": true } ] } \ No newline at end of file